Skip to content

Node, Deno, Bun and Lambda ​

@sealpixel/render runs on Node 24 or later, Deno and Bun. It depends on @sealpixel/core and @sealpixel/wasm only. The node, deno and bun conditions of @sealpixel/wasm all load the core from disk.

Node, Deno and Bun ​

Install the package and import it. The core loads from node_modules/@sealpixel/wasm on the first render, with no option.

ts
import { createRenderer } from '@sealpixel/render';
import { defaultPlugins } from '@sealpixel/plugins';

const renderer = createRenderer({ plugins: defaultPlugins });

Deno needs --allow-read, because the loader reads the core binary from disk.

The renderer loads no font or image. Read the ones your logs use and pass the bytes as assets, see Assets.

The package's own tests render the goldens on all three runtimes, against the built package.

A bundle, for example on AWS Lambda ​

A bundler moves the code of @sealpixel/wasm into your bundle, but it does not copy the binary sealpixel_core_bg.wasm. Without node_modules beside the bundle, the first render then fails with Cannot find package '@sealpixel/wasm'.

Do these two steps:

  1. Copy node_modules/@sealpixel/wasm/dist/generated/sealpixel_core_bg.wasm next to the bundle.
  2. Give its path to wasm.module.
ts
import { fileURLToPath } from 'node:url';
import { createRenderer } from '@sealpixel/render';
import { defaultPlugins } from '@sealpixel/plugins';

const here = (name: string) => fileURLToPath(new URL(`./${name}`, import.meta.url));

// Outside the handler, so a warm function reuses it.
const renderer = createRenderer({
  plugins: defaultPlugins,
  wasm: { module: here('sealpixel_core_bg.wasm') },
});

export const handler = async (event) => {
  const result = await renderer.render({ /* original, editLog, upTo, output */ });
  // Return or store result.bytes the way your function answers.
};

wasm.module also accepts the bytes or a compiled WebAssembly.Module.

Fonts in a bundle ​

The renderer loads no asset, so the bundle carries the fonts like the binary. Copy node_modules/@sealpixel/plugin-annotate/assets/Roboto-Regular.ttf next to the bundle. Read it once, outside the handler, and pass the bytes:

ts
import { readFileSync } from 'node:fs';
import { DEFAULT_FONT, defaultPlugins } from '@sealpixel/plugins';

const renderer = createRenderer({
  plugins: defaultPlugins,
  wasm: { module: here('sealpixel_core_bg.wasm') },
  assets: [{ id: DEFAULT_FONT.id, bytes: new Uint8Array(readFileSync(here('Roboto-Regular.ttf'))) }],
});

Do the same for every other font or image that your logs use. assetDeclarations(plugins) lists them.

What was tested ​

esbuild 0.28 bundled a handler like the one above with --bundle --platform=node --format=esm. The bundle, sealpixel_core_bg.wasm and the font went into an empty directory with no node_modules above it. Node 24 ran the handler there, and the hashes of the goldens ops/full-pipeline, ops/redact-rect-pixelate and ops/annotate-text-roboto matched the recorded hashes.

Give the function memory for the largest original you accept, see Memory.