posterbox
posterbox · effectsPOSTERBOX — 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).
the faceplate
inputs
| id | cable | what it does |
|---|---|---|
in | video | The 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 |
depth | cv | CV 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) |
dither | cv | CV 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) |
mix | cv | CV 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
| id | cable | what it does |
|---|---|---|
out | video | The 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
| id | label | range | default | curve |
|---|---|---|---|---|
depth | Depth | 0..? | — | discrete |
dither | Dither | 0..1 | — | linear |
mix | Mix | 0..1 | — | linear |
controls
| control | what it does |
|---|---|
| Depth | Depth — 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. |
| Dither | Dither — 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. |
| Mix | Mix — 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.