npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

wasm-html-to-image

v0.1.4

Published

HTML to image converter on a single unified WASM module

Readme

wasm-html-to-image

npm version npm downloads license Playground Documentation Samples

A high-performance, WebAssembly-powered HTML-to-image renderer and image optimization engine. It consolidates HTML rendering (Satoru / Skia / litehtml) and native image encoding (WebP / AVIF / JPEG) into a single, unified WASM binary.

No headless browsers (Chromium, Puppeteer, or Playwright) required. Runs seamlessly across Node.js, Cloudflare Workers, Edge runtimes, browsers, and the CLI.


✨ Features

  • ⚡ Single Unified WASM Binary: HTML parsing, CSS layout, vector painting, and image encoding execute entirely inside WebAssembly without copying intermediate framebuffers across JS boundaries.
  • 🎨 Versatile Inputs: Renders HTML strings, remote Web URLs, multi-page HTML arrays, or raw image buffers (PNG, JPEG, WebP, GIF, AVIF, BMP, Data URLs).
  • 📦 Rich Output Formats: Direct SVG vector streams, high-quality PNG, JPEG, WebP, AVIF, JXL, uncompressed RAW pixels, ThumbHash placeholders, and multi-page vector PDFs.
  • 🚀 Zero-Config Single Bundle: Use wasm-html-to-image/single to call render() immediately without manual WASM loading or build setup.
  • 🌐 Edge & Serverless Native: Dedicated subpaths (/workerd for Cloudflare Workers, /edge-light for Vercel Edge) tailored for strict serverless constraints.
  • 🧵 Multi-Threaded Worker Pool: Process thousands of images in parallel using wasm-html-to-image/workers (Node.js worker threads / Web Workers).
  • ⚛️ Ecosystem Ready: First-class support for React / Preact JSX components and Tailwind CSS inlined styling.

📦 Installation

npm install wasm-html-to-image

🚀 Quick Start

Zero-Config Rendering (wasm-html-to-image)

The easiest way to get started. WASM is embedded and loaded automatically (in Cloudflare Workers, the workerd-optimized module is automatically selected):

import { render } from "wasm-html-to-image";

// 1. Render HTML to PNG (returns RenderResult with .data and dimensions)
const result = await render({
  value: `<div style="background: linear-gradient(135deg, #667eea, #764ba2); padding: 40px; color: white; border-radius: 16px; font-family: sans-serif;">
    <h1 style="margin: 0; font-size: 32px;">Hello wasm-html-to-image</h1>
    <p style="margin-top: 8px; opacity: 0.9;">High-performance serverless rendering</p>
  </div>`,
  width: 800,
  format: "png",
});

console.log(result.data); // Uint8Array
console.log(result.width, result.height); // 800, 600

// 2. Convert and resize image with the same render() function
const webp = await render({
  value: result.data, // Pass raw image buffer directly
  width: 400, // Target resized width
  format: "webp",
  quality: 80,
});

console.log(webp.originalWidth, webp.originalHeight); // Original image dimensions
console.log(webp.width, webp.height); // Output image dimensions

🖼️ Image-to-Image Conversion & Optimization

wasm-html-to-image is not only an HTML renderer — it also serves as a high-speed, self-contained image-to-image converter, resizer, and compressor without requiring heavy native libraries like Sharp or ImageMagick.

Simply pass raw image bytes (Uint8Array or Buffer) or a data:image/... URL to render(). It automatically identifies input magic bytes, skips the HTML layout pass, and routes straight into the native Skia image pipeline. Every call returns rich metadata (width, height, originalWidth, originalHeight, format, isAnimated) along with the output data in .data:

import fs from "node:fs/promises";
import { render } from "wasm-html-to-image/single";

const imageBuffer = await fs.readFile("photo.jpg");

// 1. Convert JPEG -> AVIF (highest compression)
const avif = await render({
  value: imageBuffer,
  width: 800,
  format: "avif",
  quality: 75,
  speed: 6, // 0 (best quality) - 10 (fastest)
});
await fs.writeFile("photo.avif", Buffer.from(avif.data));

// 2. Crop & Resize -> PNG
const cropped = await render({
  value: imageBuffer,
  crop: { x: 50, y: 50, width: 300, height: 300 },
  width: 150,
  height: 150,
  format: "png",
});
await fs.writeFile("cropped.png", Buffer.from(cropped.data));

// 3. Generate ThumbHash Blur Placeholder
const thumbhash = await render({
  value: imageBuffer,
  width: 100,
  format: "thumbhash",
});
console.log("ThumbHash bytes:", thumbhash.data);

// 4. Convert Image -> Single-Page Vector PDF
const pdf = await render({
  value: imageBuffer,
  format: "pdf",
});
await fs.writeFile("photo.pdf", Buffer.from(pdf.data));

// 5. Convert Animated GIF -> Animated WebP
const animatedWebp = await render({
  value: await fs.readFile("animation.gif"),
  format: "webp",
  animation: true,
});
await fs.writeFile("animation.webp", Buffer.from(animatedWebp.data));

📊 Format Matrix

Inputs (value / url)

| Input Type | Detection | Description | | --------------------------- | -------------------------------- | ---------------------------------------------------------- | | HTML String | Text starting with tags / markup | Standard HTML/CSS rendering | | HTML Array (string[]) | Array of HTML strings | Multi-page PDF generation (one page per item) | | URL (url) | http:// or https:// | Automatically fetches and renders remote web pages | | Image Buffer | Uint8Array / Buffer | Magic-byte recognition for PNG, JPEG, WebP, GIF, AVIF, BMP | | Data URL | data:image/*;base64,... | Inlined image data URL, routed straight to image pipeline |

Outputs (format)

All formats return a RenderResult<T> object containing .data (Uint8Array or string for SVG) and metadata (width, height, originalWidth, originalHeight, format, isAnimated):

| Format | result.data Type | HTML Input | Image Input | | ----------- | ------------------ | ---------------------------------------------- | ----------------------------- | | png | Uint8Array | Skia render → PNG encode | Decodes and converts to PNG | | jpeg | Uint8Array | Skia render → JPEG encode (quality) | Decodes and compresses JPEG | | webp | Uint8Array | Skia render → WebP encode (quality) | Decodes and compresses WebP | | avif | Uint8Array | Skia render → AVIF encode (quality, speed) | Decodes and compresses AVIF | | jxl | Uint8Array | Skia render → JXL encode (quality, speed) | Decodes and compresses JXL | | raw | Uint8Array | Uncompressed RGBA pixel bytes | Uncompressed RGBA pixel bytes | | thumbhash | Uint8Array | Computes ThumbHash from render | Computes ThumbHash from image | | svg | string | Skia vector drawing stream | Single <image> wrapper SVG | | pdf | Uint8Array | SkPDFDocument vector PDF | Single-page centered PDF |


🧩 Subpaths & Usage

Choose the optimal entry point for your application and environment:

| Subpath | Target Environment | Highlights | | ------------------------------- | ---------------------------------------- | ---------------------------------------------------------------------- | | wasm-html-to-image | Universal (Node, Edge, Cloudflare, Deno) | Zero-config default: auto routes to single/workerd, instant render() | | wasm-html-to-image/single | Node.js, Bundlers | Single bundled WASM entry point | | wasm-html-to-image/index | High-throughput servers | Explicit loadHtmlToImageModule() & manual instance reuse | | wasm-html-to-image/workerd | Cloudflare Workers | WebAssembly.Module compilation compatible | | wasm-html-to-image/edge-light | Vercel Edge Runtime | Optimized for Edge Runtime constraints | | wasm-html-to-image/workers | Node.js, Browsers | Multi-threaded worker pool for high concurrency | | wasm-html-to-image/react | React integration | Directly render React JSX element trees | | wasm-html-to-image/preact | Preact integration | Directly render Preact JSX element trees | | wasm-html-to-image/tailwind | Utility styling | Inlines UnoCSS / Tailwind classes |


💡 Practical Examples

1. High-Throughput Node.js Server (Instance Reuse)

Reuse the WASM module across incoming HTTP requests for maximum performance:

import { loadHtmlToImageModule, htmlToImage } from "wasm-html-to-image";
import express from "express";

const app = express();
const mod = await loadHtmlToImageModule(); // Load once at startup

app.get("/ogp", async (req, res) => {
  const result = await htmlToImage(mod, {
    value: `<h1>${req.query.title}</h1>`,
    width: 1200,
    height: 630,
    format: "webp",
    quality: 85,
  });

  res.type("image/webp").send(Buffer.from(result.data));
});

app.listen(3000);

2. Cloudflare Workers (workerd)

Generate dynamic social preview images at the edge:

// wrangler.jsonc: no "rules" needed on current Wrangler (v3/v4) —
// `import ... from "*.wasm"` is bundled as WebAssembly.Module by default.
// Minimal wrangler.jsonc: { "main": "src/index.ts", "compatibility_date": "2026-09-01" }
// (Only configure "rules" yourself if you customize bundling rules or use a very old Wrangler.
// Note: @cloudflare/vite-plugin ignores "rules".)

import { render } from "wasm-html-to-image"; // auto-selects the workerd build on Cloudflare ("wasm-html-to-image/workerd" also works)

export default {
  async fetch(request: Request): Promise<Response> {
    const result = await render({
      value: `<div style="padding: 40px; font-family: sans-serif; background: #0f172a; color: white;">
        <h1>Edge OGP Generator</h1>
      </div>`,
      width: 1200,
      height: 630,
      format: "png",
    });

    return new Response(result.data, {
      headers: {
        "Content-Type": "image/png",
        "Cache-Control": "public, max-age=86400",
      },
    });
  },
};

3. Multi-Threaded Batch Generation (workers)

Leverage multi-core CPUs to process large batches of images in parallel:

import { render } from "wasm-html-to-image/workers";

const tasks = items.map((item) =>
  render({
    value: `<h1>${item.title}</h1>`,
    width: 600,
    height: 400,
    format: "webp",
  }),
);

const images = await Promise.all(tasks);

4. Command Line Interface (CLI)

Render directly from your shell without writing code:

# Render HTML to WebP
npx wasm-html-to-image template.html -o banner.webp -w 1200 -h 630 -f webp -q 90

# Capture screenshot from URL
npx wasm-html-to-image https://example.com -o site.png -w 1280 -h 720

# Convert and resize an image
npx wasm-html-to-image photo.jpg -o photo.avif -w 800 -f avif -q 80

📖 Documentation & Examples

For full guides, architecture deep-dives, font management, and advanced recipes:

👉 Read the Official Documentation
👉 Sample Code & Projects (GitHub)


🛠️ Development

Prerequisites

  • Node.js >= 20, pnpm >= 9
  • Emscripten SDK (emsdk), CMake, Ninja, vcpkg (for C++ WASM builds)

Building

# Install workspace dependencies
pnpm install

# Build TypeScript packages
pnpm --filter wasm-html-to-image build
pnpm build:js

# Build Documentation
pnpm docs:build

Testing

# Unit tests (Vitest)
pnpm --filter wasm-html-to-image test

# Visual regression tests
pnpm --filter visual-test test

📄 License

MIT © SoraKumo