WASM Module
The @stiwi/pixelsrc-wasm package provides a WebAssembly build of the Pixelsrc renderer for use in browsers and Node.js.
Installation
npm install @stiwi/pixelsrc-wasm
Or with other package managers:
yarn add @stiwi/pixelsrc-wasm
pnpm add @stiwi/pixelsrc-wasm
Browser Usage
import init, { render_to_png, render_to_rgba, list_sprites } from '@stiwi/pixelsrc-wasm';
// Initialize WASM module (required before first use)
await init();
// Define a sprite in pixelsrc format
const jsonl = `{
type: "sprite",
name: "heart",
size: [7, 5],
palette: { _: "transparent", r: "#FF0000" },
regions: {
r: {
union: [
{ rect: [1, 0, 2, 1] },
{ rect: [4, 0, 2, 1] },
{ rect: [0, 1, 7, 1] },
{ rect: [1, 2, 5, 1] },
{ rect: [2, 3, 3, 1] },
{ rect: [3, 4, 1, 1] },
],
z: 0,
},
},
}`;
// Render to PNG bytes
const pngBytes = render_to_png(jsonl);
// Display in an <img> element
const blob = new Blob([pngBytes], { type: 'image/png' });
const url = URL.createObjectURL(blob);
document.getElementById('preview').src = url;
Canvas Rendering
For direct canvas rendering, use render_to_rgba:
const result = render_to_rgba(jsonl);
const imageData = new ImageData(
new Uint8ClampedArray(result.pixels),
result.width,
result.height
);
ctx.putImageData(imageData, 0, 0);
Node.js Usage
import { readFileSync, writeFileSync } from 'fs';
import init, { render_to_png } from '@stiwi/pixelsrc-wasm';
await init();
const jsonl = readFileSync('sprite.pxl', 'utf8');
const pngBytes = render_to_png(jsonl);
writeFileSync('sprite.png', pngBytes);
API Reference
init(): Promise<void>
Initialize the WASM module. Must be called once before using any other function.
render_to_png(jsonl: string, spriteName?: string): Uint8Array
Render JSONL input to PNG bytes.
| Parameter | Type | Description |
|---|---|---|
jsonl | string | Pixelsrc JSONL content |
spriteName | string? | Optional: render specific sprite |
Returns: PNG image as Uint8Array
render_to_rgba(jsonl: string, spriteName?: string): RenderResult
Render to raw RGBA pixel data for canvas manipulation.
Returns:
| Property | Type | Description |
|---|---|---|
width | number | Image width in pixels |
height | number | Image height in pixels |
pixels | Uint8Array | Raw RGBA pixel data |
warnings | string[] | Any rendering warnings |
list_sprites(jsonl: string): string[]
Get list of sprite and composition names defined in the input.
validate(jsonl: string): string[]
Validate JSONL without rendering. Returns array of error/warning messages. Empty array means valid input.
Build Targets
The WASM module supports multiple bundler targets:
| Target | Use Case | Build Command |
|---|---|---|
web | Browser ES modules | npm run build |
nodejs | Node.js | npm run build:nodejs |
bundler | Webpack/Rollup | npm run build:bundler |
Error Handling
try {
const pngBytes = render_to_png(jsonl);
// Success
} catch (err) {
// Handle parsing or rendering errors
console.error('Render failed:', err.message);
}
Common errors:
- Invalid JSON syntax
- Undefined palette tokens
- Missing required fields
Performance Tips
- Initialize once: Call
init()once at app startup, not per render - Reuse results: Cache PNG blobs when displaying the same sprite multiple times
- Use RGBA for animations:
render_to_rgbais faster for frequent canvas updates - Batch validation: Use
validate()for quick syntax checks before rendering
Framework Integration
React
import { useEffect, useState, useRef } from 'react';
import init, { render_to_png } from '@stiwi/pixelsrc-wasm';
function PixelSprite({ jsonl }) {
const [imgUrl, setImgUrl] = useState(null);
const initRef = useRef(false);
useEffect(() => {
async function render() {
if (!initRef.current) {
await init();
initRef.current = true;
}
const png = render_to_png(jsonl);
const blob = new Blob([png], { type: 'image/png' });
setImgUrl(URL.createObjectURL(blob));
}
render();
}, [jsonl]);
return imgUrl ? <img src={imgUrl} alt="Pixel sprite" /> : null;
}
Vue
<template>
<img v-if="imgUrl" :src="imgUrl" alt="Pixel sprite" />
</template>
<script setup>
import { ref, watch, onMounted } from 'vue';
import init, { render_to_png } from '@stiwi/pixelsrc-wasm';
const props = defineProps(['jsonl']);
const imgUrl = ref(null);
let wasmReady = false;
onMounted(async () => {
await init();
wasmReady = true;
render();
});
watch(() => props.jsonl, render);
function render() {
if (!wasmReady) return;
const png = render_to_png(props.jsonl);
const blob = new Blob([png], { type: 'image/png' });
imgUrl.value = URL.createObjectURL(blob);
}
</script>
Browser Compatibility
The WASM module requires a modern browser with WebAssembly support.
Supported Browsers
| Browser | Minimum Version | Notes |
|---|---|---|
| Chrome | 90+ | Full support |
| Firefox | 88+ | Full support |
| Safari | 14+ | Full support |
| Edge | 90+ | Full support (Chromium-based) |
Required Features
The WASM module relies on the following browser APIs:
- WebAssembly - Core requirement for running the compiled Rust code
- ES6 Modules - Dynamic import for loading the WASM module
- Blob API - For creating image URLs from PNG bytes
- URL.createObjectURL - For displaying rendered images
- Promises - For async initialization
Feature Detection
The documentation demos include built-in feature detection:
// Check if WASM is supported
if (typeof WebAssembly === 'object' &&
typeof WebAssembly.instantiate === 'function') {
// WASM is supported
}
// Using the pixelsrcDemo API (in documentation pages)
const support = pixelsrcDemo.getBrowserSupport();
if (support.isCompatible) {
// All required features available
} else {
console.log('Missing:', support);
}
Fallback Behavior
When running in an unsupported browser:
- Demo containers display a helpful error message
- The page remains functional (text content, navigation)
- Static images are used where possible as fallbacks
Cross-Browser CSS
The documentation uses cross-browser compatible CSS for pixel art rendering:
/* Pixel-perfect image scaling */
img {
image-rendering: -moz-crisp-edges; /* Firefox */
image-rendering: -webkit-optimize-contrast; /* Safari */
image-rendering: pixelated; /* Chrome, Edge */
image-rendering: crisp-edges; /* Standard */
}
Testing Checklist
When deploying WASM-based features, verify:
- Chrome (latest and 2 versions back)
- Firefox (latest and 2 versions back)
- Safari (latest)
- Edge (latest)
- Mobile Safari (iOS 14+)
- Chrome for Android (latest)
Known Issues
- Safari 13 and earlier: No WebAssembly SIMD support (module loads but may be slower)
- IE11: Not supported (no WebAssembly)
- Some corporate proxies: May block
.wasmfile downloads
Chronicle Integration
Chronicle is a narrative engine for games and graphic novels that uses pixelsrc as its asset rendering layer.
How Chronicle Uses Pixelsrc
Chronicle embeds pixelsrc WASM internally to:
- Pre-render character sprites (all emotions/variants)
- Pre-render location backgrounds
- Generate animation frames for portraits
- Compose scenes (background + positioned characters)
- Render comic panels
Game clients interact with Chronicle, not pixelsrc directly:
Game Client → Chronicle (WASM) → Pixelsrc (embedded)
│
└─► Pre-rendered PNGs
Asset Flow
- Game generates pixelsrc JSONL (via AI or authoring)
- Chronicle receives:
createCharacter(def, pixelsrc_jsonl) - Chronicle stores the JSONL
- Chronicle calls pixelsrc to render → caches PNG
- Later: Chronicle returns cached PNG on request
Why Embed vs. Direct Use
Chronicle embeds pixelsrc rather than exposing it to clients because:
- Pre-rendering: Assets rendered once at creation, not on demand
- Caching: Chronicle manages the render cache
- Simplicity: Clients only deal with PNGs, not JSONL
- Consistency: All rendering goes through one path
Generating Assets for Chronicle
When generating assets for use with Chronicle, follow the standard pixelsrc format:
{
type: "palette",
name: "hero_colors",
colors: {
skin: "#FFDAB9",
hair: "#8B4513",
armor: "#4682B4",
},
}
{
type: "sprite",
name: "hero_neutral",
size: [32, 48],
palette: "hero_colors",
regions: { ... },
}
{
type: "sprite",
name: "hero_happy",
size: [32, 48],
palette: "hero_colors",
regions: { ... },
}
{
type: "animation",
name: "hero_talk",
frames: ["hero_talk_1", "hero_talk_2"],
duration: 100,
}
Chronicle will parse this and render each sprite/animation to its cache.
Related
- Web Editor - Browser-based editor using WASM
- Obsidian Plugin - Uses WASM for rendering