Pretext-inspired pixel animation. Compile once, paint cheap, render anywhere.
Same compiled sprite, painted in parallel by Canvas2D, SVG, and ASCII.
Why is per-frame paint cheap? Most cells don't change. The right panel shows only the cells that get repainted on each frame — everything else is left untouched.
A typical frame repaints 2–5% of cells. The compiled diffs[i] buffer
is a packed Int16Array; the renderer is just a tight 3-step for loop
over [x, y, color] triples.
The setup/hot-path split: heavy work happens once, painting stays sub-millisecond.
Measuring…
withPalette() is O(palette size). It returns a new compiled
sprite that reuses diffs and grids by reference — no pixels are recomputed.
Compare against a full compile() recompile of the same source with the new colors.
Click "Run swap benchmark" to compare.
Many sprites, two paint strategies. Toggle between PixelFlow's diff-only hot path and a naive full-redraw baseline. Watch the FPS gap widen as instance count climbs.
Three paint strategies, same compiled sprite: Diff walks per-frame diff ops (best for single-sprite mostly-static animations); Raster pre-rasterizes each frame to an offscreen canvas and drawImages it (best for many instances); Naive walks every non-transparent cell every frame (baseline). At ~5000 instances Raster sustains 60 fps where Diff drops below 30 — the diff path trades batch efficiency for per-cell granularity.
Drop a PNG (or sprite-sheet). PixelFlow will quantize colors, build a single-char palette, and slice it into frames.
import {
compile, createAnimator,
paintCanvas, // diff-op (cheapest per-frame for single sprite)
paintRaster, // pre-raster + drawImage (best for many instances)
prepareSVG, paintSVG, paintAscii,
} from 'pretext-pixel';
const sprite = compile({
frames: [
[
"..RRRR..",
".RRRRRR.",
".R.RR.R.",
"..RRRR..",
],
// ...more frames
],
palette: { R: '#dc2626' },
speed: 120,
});
const ctx = document.querySelector('canvas').getContext('2d');
const svgState = prepareSVG(document.querySelector('svg'), sprite);
const pre = document.querySelector('pre');
const anim = createAnimator(sprite, [
c => paintCanvas(ctx, sprite, c),
c => paintSVG(svgState, sprite, c),
c => paintAscii(pre, sprite, c),
]);
anim.start();