posterbox

posterbox · effects

POSTERBOX — retro PALETTE-CRUSH (posterizer) video PROCESSOR. Takes a video input and truncates every pixel to an authentic retro per-channel bit allocation — the classic "8-bit"/"hi-colour" palette crush. This is the dedicated home of the per-channel RGB posterize looks CELLSHADE's rebuild dropped (they are posterization, not cel shading): the 8-bit RGB 3-3-2 and 16-bit RGB 5-6-5 floor-quantization paths are ported EXACTLY, so an old cellshade retro patch recreates byte-for-byte by swapping in POSTERBOX. "Tints neutral grays" is a FEATURE, not a bug — an asymmetric allocation has different level grids per channel, so gray 0.2 at 3-3-2 quantizes to (36,36,0), a dark olive; that channel-clipped cast IS the period-correct look. DEPTH is a 5-step DISCRETE knob (the param is the step INDEX 0..4; the card shows the allocation name + palette size, and the matching CV input uses a discrete cvScale so it snaps to the 5 steps) stepping the ladder: 1-1-1 = 8 colours (3-bit RGB, ZX-Spectrum brutal), 2-2-2 = 64 (EGA master palette), 3-3-2 = 256 (VGA-era 8-bit truecolor, the default), 4-4-4 = 4096 (Amiga OCS), 5-6-5 = 65536 (RGB565 hi-colour, subtle). DITHER (0..1, default 0) is the classic companion: a Bayer 4×4 ordered dither that perturbs the quantizer threshold per screen pixel (the same offset-before-truncate scheme the PlayStation used before truncating to 15-bit) — at 0 the palette bands are HARD (the pure legacy crush); raising it dissolves band edges into alternating checkered pixels so gradients render as retro cross-hatch instead of banding. MIX (0..1, default 1) is a straight dry/wet. DEPTH / DITHER / MIX each have a matching CV input (port id == param id). STATELESS per frame — one texture sample + one Bayer lookup per pixel (single-pass shader, no neighbourhood taps). IN takes RGB; OUT is video — patch a source (CAMERA / VIDEOBOX / SHAPES / a generator) → IN, OUT → OUTPUT or a video mixer; step DEPTH for the era, raise DITHER for the cross-hatch, back off MIX to soften. All ports live on the yellow drill-down PATCH PANEL (no raw side jacks). The pure quantizer + Bayer math is unit-tested in $lib/video/modules/posterbox (the exact CPU mirror of the shader).

posterbox is a retro palette crusher: it truncates every pixel of the incoming video to an authentic per-channel bit allocation, reproducing the look of real palette-era hardware. The Depth ladder steps through five allocations — 1-1-1 (8 colours, ZX-Spectrum-style brutal posterize), 2-2-2 (64 colours, the EGA master palette), 3-3-2 (256 colours, the VGA-era 8-bit truecolor split), 4-4-4 (4096 colours, Amiga OCS), and 5-6-5 (65536 colours, RGB565 hi-colour). The 3-3-2 and 5-6-5 steps are the exact per-channel floor quantizers CELLSHADE's original 8-bit/16-bit retro modes used, ported unchanged, so old cellshade retro patches recreate byte-for-byte here. Because the asymmetric allocations give each channel a different level grid, neutral grays come out slightly tinted (gray 0.2 at 3-3-2 becomes a dark olive) — that channel-clipped cast is the period-correct look and is intentional. Dither adds the classic companion: a Bayer 4×4 ordered dither that perturbs the quantizer threshold per screen pixel, so smooth gradients render as retro cross-hatch instead of hard bands (the same offset-before-truncate scheme the PlayStation used for its 15-bit output). Mix is a straight dry/wet. The effect is stateless per frame — one texture sample and one Bayer lookup per pixel, no feedback and no neighbourhood taps.

the faceplate

posterboxinvideodepthcvdithercvmixcvoutvideoaudiocvgatepitch
4 inputs · 1 outputs · 3 params

inputs

idcablewhat it does
invideoThe RGB video source to palette-crush. Each channel is quantized to the Depth step's per-channel level count; with no input the output is solid black.
RGB video stream
depthcvCV input that modulates Depth using a discrete cvScale, so the CV snaps to the 5 bit-allocation steps (1-1-1 / 2-2-2 / 3-3-2 / 4-4-4 / 5-6-5) rather than sweeping continuously.
control voltage (CV); modulates depth (integer buckets — CV selects a discrete step)
dithercvCV input that modulates Dither, sweeping the Bayer ordered-dither amount linearly over its full 0..1 range — higher CV dissolves the hard palette bands into cross-hatch.
control voltage (CV); modulates dither (additive offset — ±1 CV sweeps the full range, centered on the knob)
mixcvCV input that modulates Mix, sweeping the dry/wet linearly over its full 0..1 range — 0 is the untouched source, 1 the full palette crush.
control voltage (CV); modulates mix (additive offset — ±1 CV sweeps the full range, centered on the knob)

outputs

idcablewhat it does
outvideoThe palette-crushed video frame: the source truncated to the chosen retro bit allocation, dithered by the Bayer amount, blended dry/wet by Mix.
RGB video stream

params

idlabelrangedefaultcurve
depthDepth0..?discrete
ditherDither0..1linear
mixMix0..1linear

controls

controlwhat it does
DepthDepth — a 5-step discrete ladder of per-channel bit allocations: 1-1-1 (8 colours), 2-2-2 (64), 3-3-2 (256, the legacy 8-bit look, default), 4-4-4 (4096), 5-6-5 (65536, the legacy 16-bit look). Low steps are brutal posterize; high steps a subtle hi-colour rounding. The card shows the allocation name and palette size.
DitherDither — Bayer 4×4 ordered-dither amount (0..1, default 0). At 0 the palette bands are hard (the pure legacy crush); raising it perturbs the quantizer per screen pixel so band edges dissolve into alternating checkered pixels and gradients read as retro cross-hatch.
MixMix — dry/wet (0..1, default 1). 1 is the full crush, 0 passes the source through untouched; in between blends the crushed and clean frames linearly.

source

posterbox.ts on GitHub.

Generated from packages/web/src/lib/{audio,video}/module-registry.ts · repo