TakumiTakumi

WebAssembly (In Browser)

Integrate Takumi with WebAssembly for client-side image generation.

For server-side rendering, consider using the Node.js or Bun Integration for improved performance and compatibility.

Takumi provides WebAssembly bindings for browser compatibility, allowing client-side image generation. The WASM binary is downloaded client-side.

npm i @takumi-rs/wasm @takumi-rs/helpers

Constraints

In order to keep the binary size minimal, WebAssembly version dropped some features compare to @takumi-rs/core.

  • Only synchronous API
  • woff font format is not supported, (but woff2 is supported)
  • No default fonts are bundled

Load the WASM Module

Since WASM files are binary, they need to be loaded and initialized properly. The Takumi WASM package provides an init function that takes the URL of the WASM binary.

Different bundlers and frameworks have different ways to handle WASM files. Below are examples for popular setups.

Cloudflare Workers Setup

We have a dedicated guide for Cloudflare Workers Setup.

Vite Setup

import init, { Renderer } from "@takumi-rs/wasm";
import wasmUrl from "@takumi-rs/wasm/takumi_wasm_bg.wasm?url";

await init(wasmUrl);

const renderer = new Renderer();

Webpack Setup

To use Takumi WASM with Webpack, you need to configure Webpack to handle .wasm files as assets. Add the following to your webpack.config.js:

module.exports = {
  module: {
    rules: [
      {
        test: /\.wasm$/,
        type: 'asset/resource',
        generator: {
          filename: 'wasm/[name].[hash][ext]'
        }
      }
    ]
  }
};

Then, in your code, import the WASM file and initialize the module:

import init, { Renderer } from '@takumi-rs/wasm';
import wasmUrl from '@takumi-rs/wasm/takumi_wasm_bg.wasm';

await init(wasmUrl);
const renderer = new Renderer();

Next.js Setup

Next.js (with Webpack 5+) also supports importing WASM as assets. You can use the same Webpack rule as above by customizing your next.config.js:

// next.config.js
module.exports = {
  webpack(config) {
    config.module.rules.push({
      test: /\.wasm$/,
      type: 'asset/resource',
      generator: {
        filename: 'static/wasm/[name].[hash][ext]'
      }
    });
    return config;
  },
};

Then, import and initialize the WASM module in your component or page:

import init, { Renderer } from '@takumi-rs/wasm';
import wasmUrl from '@takumi-rs/wasm/takumi_wasm_bg.wasm';

useEffect(() => {
  (async () => {
    await init(wasmUrl);
    const renderer = new Renderer();
    // ... your code ...
  })();
}, []);

No Bundler Setup

For direct browser usage without a bundler, download files locally or use a CDN like unpkg.

Usage

import { container, text, percentage } from "https://unpkg.com/@takumi-rs/helpers";
import init, { Renderer } from "https://unpkg.com/@takumi-rs/wasm";

await init("http://unpkg.com/@takumi-rs/wasm/takumi_wasm_bg.wasm");

const renderer = new Renderer();

First, initialize a Renderer instance to render images.

import init, { Renderer } from "@takumi-rs/wasm";
import wasmUrl from "@takumi-rs/wasm/takumi_wasm_bg.wasm?url";

await init(wasmUrl);

const renderer = new Renderer();

// load fonts using renderer.loadFont()
// load images using renderer.putPersistentImage()

Create a function to construct layout.

you can take existing React component with fromJsx function.

import { container, text } from "@takumi-rs/helpers";

function createOpenGraphImage(name: string) {
  return container({
    width: 1200,
    height: 630,
    backgroundColor: 0x000000,
    children: [
      text(`Hello, ${name}!`, {
        fontSize: 48,
        color: 0xffffff,
        x: 100,
        y: 100,
      }),
    ],
  });
}

Finally, render the layout to an image buffer.

const imageBuffer = await renderer.render(
  // root node
  createOpenGraphImage("John Doe"),
  // width
  1200,
  // height
  630,
  // format
  "webp",
);

// Data URL rendering is also supported through renderer.renderAsDataUrl()

Congratulations, now you have Takumi setup! You can try further to offload it to Web Workers to not block the main thread.

If you are interested in more advanced usages of Takumi, consider take a look at Deep Dives section.