Create social media images locally in your browser. Share the link. Nothing is
uploaded.
This ES module uses static WebAssembly instance imports and includes SVG, PNG,
and WebP output. Remove imports and output calls that your application does not use.
import * as ogImageRenderer from "./text-to-og-image-svg-inter.wasm";
import * as svgRasterizer from "./svg-rasterize-to-bmp-b8g8r8a8-srgb.wasm";
import * as pngEncoder from "./bmp-to-png.wasm";
import * as webpEncoder from "./bmp-b8g8r8a8-srgb-to-webp-lossless.wasm";
const encoder = new TextEncoder();
const decoder = new TextDecoder("utf-8", { fatal: true });
function renderOutput(exports, inputSize) {
const result = BigInt.asUintN(64, exports.render(inputSize));
if ((result >> 63n) !== 0n) throw new Error("component rejected input");
return {
size: Number(result & 0xffff_ffffn),
ptr: Number((result >> 32n) & 0x7fff_ffffn),
};
}
export function renderOGImageToSVG({
title = "",
subtitle = "",
textColor = 0xffffffff,
backgroundColor = 0x4b2e83ff,
fontMaxSize = 112,
fontWeight = 700,
} = {}) {
const form = new URLSearchParams({ title, subtitle }).toString();
const input = new Uint8Array(
ogImageRenderer.memory.buffer,
ogImageRenderer.input_ptr(),
ogImageRenderer.input_utf8_cap(),
);
const { read, written } = encoder.encodeInto(form, input);
if (read !== form.length) {
throw new RangeError("Open Graph image input is too large");
}
ogImageRenderer.uniform_set_text_color_rgba(textColor);
ogImageRenderer.uniform_set_background_color_rgba(backgroundColor);
ogImageRenderer.uniform_set_font_max_size(fontMaxSize);
ogImageRenderer.uniform_set_font_weight(fontWeight);
const output = renderOutput(ogImageRenderer, written);
if (output.size > ogImageRenderer.output_utf8_cap()) {
throw new RangeError("Open Graph image output is too large");
}
return decoder.decode(new Uint8Array(
ogImageRenderer.memory.buffer,
output.ptr,
output.size,
));
}
export function renderOGImageToPNG(options) {
const svg = renderOGImageToSVG(options);
const input = new Uint8Array(
svgRasterizer.memory.buffer,
svgRasterizer.input_ptr(),
svgRasterizer.input_utf8_cap(),
);
const { read, written } = encoder.encodeInto(svg, input);
if (read !== svg.length) {
throw new RangeError("Open Graph SVG is too large to rasterize");
}
const bmpOutput = renderOutput(svgRasterizer, written);
if (bmpOutput.size > svgRasterizer.output_bytes_cap()) {
throw new RangeError("Open Graph BMP output is too large");
}
const bmp = new Uint8Array(
svgRasterizer.memory.buffer,
bmpOutput.ptr,
bmpOutput.size,
);
if (bmp.length > pngEncoder.input_bytes_cap()) {
throw new RangeError("Open Graph BMP input is too large");
}
new Uint8Array(
pngEncoder.memory.buffer,
pngEncoder.input_ptr(),
bmp.length,
).set(bmp);
const pngOutput = renderOutput(pngEncoder, bmp.length);
if (pngOutput.size > pngEncoder.output_bytes_cap()) {
throw new RangeError("Open Graph PNG output is too large");
}
return new Uint8Array(
pngEncoder.memory.buffer,
pngOutput.ptr,
pngOutput.size,
).slice();
}
export function renderOGImageToWebp(options) {
const svg = renderOGImageToSVG(options);
const input = new Uint8Array(
svgRasterizer.memory.buffer,
svgRasterizer.input_ptr(),
svgRasterizer.input_utf8_cap(),
);
const { read, written } = encoder.encodeInto(svg, input);
if (read !== svg.length) {
throw new RangeError("Open Graph SVG is too large to rasterize");
}
const bmpOutput = renderOutput(svgRasterizer, written);
if (bmpOutput.size > svgRasterizer.output_bytes_cap()) {
throw new RangeError("Open Graph BMP output is too large");
}
const bmp = new Uint8Array(
svgRasterizer.memory.buffer,
bmpOutput.ptr,
bmpOutput.size,
);
if (bmp.length > webpEncoder.input_bytes_cap()) {
throw new RangeError("Open Graph BMP input is too large");
}
new Uint8Array(
webpEncoder.memory.buffer,
webpEncoder.input_ptr(),
bmp.length,
).set(bmp);
const webpOutput = renderOutput(webpEncoder, bmp.length);
if (webpOutput.size > webpEncoder.output_bytes_cap()) {
throw new RangeError("Open Graph WebP output is too large");
}
return new Uint8Array(
webpEncoder.memory.buffer,
webpOutput.ptr,
webpOutput.size,
).slice();
}
const options = {
title: "Reusable components",
subtitle: "Rendered locally with embedded Inter paths",
textColor: 0xffffffff,
backgroundColor: 0x4b2e83ff,
fontMaxSize: 112,
fontWeight: 700,
};
const svg = renderOGImageToSVG(options);
const png = renderOGImageToPNG(options);
const webp = renderOGImageToWebp(options);
console.log({ svg, png, webp });