Receipt
A thermal receipt printer, for Resolume

What it does
An FFGL effect for Resolume Arena and Avenue. A thermal printer has a fixed line of resistive heaters, 576 across an 80 mm roll or 384 across 58 mm, and no ink: heat-sensitive paper darkens wherever it gets hot enough. The head prints one dot row at a time as the stepper pulls the paper through, so the look is all down to how heat behaves, and Receipt runs that row by row.
Tone is a dither, because a dot's either on or off: an 8 by 8 Bayer matrix, Floyd-Steinberg or Atkinson, in raster order the way a printer's firmware does it. Each heater is a one-pole: one that fired on the last row is still warm, so the next dot there comes out darker and the heat spreads into the paper beside it. Vertical lines thicken by a dot each side, every mark trails a tail below it, and dark areas block up. History Control is the firmware's fix. It shortens a warm heater's strobe by however much heat it reckons is left, which stops the thickening but can't cool a heater that isn't firing.
The supply can only fire so many dots at once, so the head's strobed in blocks and a block with lots of black dots sags. Dense rows print paler, and a row whose blocks differ in coverage bands at the block boundaries, which on dark footage means a banded grey with a seam down the head. A slipping roller repeats a row, or stalls while the data carries on and overprints a dark band. Age fades the dye, yellows the paper and browns the print, darkest at the edge where it was held.
Static prints the frame as a receipt. Printing scrolls the receipt out of the slot at the print speed, with each new row printed from whatever frame was showing when the head got there, so the paper is a slit-scan of time, and tears it off at the tear length. The dither and the print run on the CPU at the head's resolution after a read-back, about 1.2 ms a frame whatever the output size, because error diffusion is serial in both directions. The paper's opaque and everything off the paper is transparent, so the layer below shows round the receipt.
- A thermal head of 384 or 576 heaters at 8 dots per millimetre, one dot row at a time
- Bayer, Floyd-Steinberg and Atkinson dithers, in raster order
- Heat carried from row to row: lines thicken, marks run on, dark areas block up; the firmware's History Control
- A supply budget strobed in 1 to 8 blocks: dense rows print paler and band at the block seams
- A slipping roller that repeats or overprints rows
- Static or Printing: the receipt scrolls out of the slot in real time and tears off
- Six paper tints, age fading and yellowing; Letterbox, Rotate or Tile
- Universal macOS bundle and a Windows x64 DLL
Where it stands
v0.1.0, and early days. It hasn't been loaded into Resolume on macOS yet. On Windows it has: a build loads, registers and renders in Resolume Arena 7.27.1 on software rendering (no GPU), with every parameter as declared and all 14 controls shown moving the picture. The printer is measured offline, by a harness driving the real plugin: each dither hits its tone on flat greys within a stated bound, heat carry widens a line and runs it on by the rows the heat model predicts while history control holds it back, an over-budget strobe block sags by the stated amount, forced slips move exactly the stated rows, Printing prints the same rows at 60, 50 and 30 fps, and a resize mid-print keeps every dot. It catches seven models broken on purpose and a one-character sabotage of its own shader, and all 14 controls change the picture. The printer's constants (the paper's threshold, how far the heat spreads, the supply's dot budget, the dye's optical density) are chosen, not measured off a real printer. The CPU print costs about 1.2 ms a frame on an M4 Max, but the read-back it waits on inside a real Resolume composition hasn't been measured. The macOS build is signed and notarised. There's a user guide and a browser demo, but no presets and no OpenFX port. Try it in your own rig before you trust it in a show.
Watch it running
Nothing loads from YouTube until you press play.Watch it there instead