Skip to content

Releases: stoatworks-labs/slowscan

Release list

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 24 Sep 01:00

Slow-scan television over HF, as an FFGL effect for Resolume Arena and Avenue, shown as SW Slowscan. SSTV sends a still picture down a voice radio channel as audio: each pixel a tone, 1500 Hz for black to 2300 Hz for white, a line at a time, with a 1200 Hz pulse between lines. So the plugin builds the actual chain, source → tones → HF channel → discriminator → line timing → picture, on the CPU at 11,025 samples a second, in Martin M1, Scottie S1 and Robot 36. What reaches the screen is whatever the receiver made of it.

None of the look is drawn. A receiver clock a few ppm off slants every vertical edge, with colour fringes where one scan pulls into the next; Line Sync takes the lean away. The new picture overwrites the old from the top under a cursor. Below the FM threshold the discriminator throws clicks and the picture turns to streaks; fading shows as bands of noisy lines, multipath as ghosts, a carrier on frequency as vertical stripes. The audio routed in goes on air as interference shaped by Resolume's FFT bins. Speed runs a two-minute picture in as little as a second without changing anything per sample. The defaults are Martin M1 at 40x over a 25 dB path, a picture every three seconds.

What is verified

By tools/verify.sh on an M4 Max, macOS 26.4, on 2026-09-24, every physics check at 320×180 and 1280×720:

  • Every line start, segment boundary and pixel boundary of Martin M1, Scottie S1 and Robot 36 lands on its constant to the sample; drift over a frame is at worst 0.9997 samples, never a sum.
  • At ±100 and ±300 ppm the fitted lean is within 5e-5 px/line of e × T_line / T_pixel (tolerance 0.0023); fitted again in the rendered frame at ±150 ppm it is 0.14637 px/line against 0.14634.
  • Flat black, grey and white come back to the exact 8-bit value; an edge's tail decays with τ = 5.797 px against the one-pole's 5.797.
  • Above the knee the pixel variance is within 2% of the linearised discriminator's closed form; the knee sits at 3.70 dB SNR against Rice's 3.11 (tolerance 2.78, derived).
  • 24 lines of a noisy, fading, multipath picture are bit-identical at 1x and 120x. VIS 44, 60 and 8 decode back to Martin, Scottie and Robot; at 300 ppm, where Free-run would drift 75 px, Line Sync holds the edge within 0.24 px.
  • 14 of 14 negative controls fail as they should, and a one-character mutation of the shipped compose shader was caught by --render and --raster at both rasters.
  • All 20 swept controls change the picture; all 3 shaders compile through glslc; oxbow sees SW Slowscan / SS01 / effect and renders 120 frames.
  • Render cost at 40x 1.00 ms/frame at 720p, 1080p and 4K alike: it is the CPU signal chain, which ignores the output size. It scales with Speed, 0.31 ms at 1x and 2.50 at 120x (1080p); with multipath, QRM and audio all on, the chain needs 3.7 ms of CPU a frame at 120x, on the host's render thread. macOS figures only.
  • Windows, in Resolume Arena 7.27.1 (win-lab, Mesa llvmpipe, no GPU, 2026-09-24): a CI build of this source loads from Extra Effects, registers as SW Slowscan / SS01 / effect, all 27 host controls match the declaration, it renders and Arena's log stays clean: 9 of 9 of the fleet gate's checks. 18 controls moved the picture, 4 of them under a precondition; the 3 audio-driven controls (Audio, Audio QRM, Bin Spacing) were not testable, because win-lab has no sound device. Software rendering says nothing about a GPU or about speed.

What is not

  • Never loaded into Resolume on macOS. How its controls in four groups, one of them the audio input, present in the inspector there is untested.
  • Nobody has measured how Resolume lays out its 64 FFT bins, so Bin Spacing exposes the assumption (Linear or Log); whether 3.7 ms of CPU a frame at 120x is comfortable on a busy show machine is untested too.
  • The fading is flat and the channel is one path or two, not a multi-tap ionospheric model. The receiver is one decoder design, not MMSSTV's or QSSTV's, and when a header fails it starts the picture as an operator pressing Start would, without searching by correlation.
  • There is a browser demo: the plugin's own shaders in WebGL2 around a JavaScript port of its signal chain, with no audio input. It is a port, not the plugin.
  • No OpenFX port, no presets. There is a user guide at https://stoatworks-labs.com/software/slowscan/guide/.

The macOS build is Developer ID-signed and notarised by the release pipeline after publication; if macOS refuses a download, it predates the signing, so download it again. The Windows build is unsigned, so SmartScreen warns once.