PixelFlow

Pretext-inspired pixel animation. Compile once, paint cheap, render anywhere.

1. One compile, three renderers #

Same compiled sprite, painted in parallel by Canvas2D, SVG, and ASCII.

canvas
svg
ascii

  
Theme:

2. Diff visualizer #

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.

full render
painted cells only

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.

3. Compile vs paint timing #

The setup/hot-path split: heavy work happens once, painting stays sub-millisecond.

compile() once
— ms
paint per-frame (avg)
— ms

Measuring…

4. Palette swap timing #

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.

target sprite: bird
withPalette() (1000×)
— ms
compile() recompile (1000×)
— ms

Click "Run swap benchmark" to compare.

5. Stress field #

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.

— fps — cells/f

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.

6. PNG import #

Drop a PNG (or sprite-sheet). PixelFlow will quantize colors, build a single-char palette, and slice it into frames.

Drop a PNG here, or
Usage example
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();