Releases: Utopian-Academy/Deckboy
Release list
Deckboy v0.90.0
Any character you can type
Text mode's alphabet was whatever had been drawn as a 5x7 bitmap in the source —
about a hundred characters, and nothing outside them. Paste a row of symbols
into the custom glyphs field and the ones nobody had drawn were dropped in
silence.
Now anything you type or paste is drawn, rendered through a font on the machine:
stars, notes, flowers, arrows, box drawing, dingbats, scripts — whatever the
system has a face for. Each character is looked up across several fonts, so a
symbol missing from one is found in another instead of coming out as an empty
box, and it is rasterised once per size and cached.
Mixing is just typing: A♪★b✿ is four ordinary letters and symbols in one set,
ordered darkest to brightest like any other glyph set.
Emoji
They work, in colour, wherever the platform has a colour emoji font — Windows
and macOS have one as standard, and most Linux installs can add one. A colour
emoji keeps its own colours and ignores the ink setting, which is the whole
point of drawing one.
Also
The glyph set list gains font (type anything), which forces the font path
even for plain letters. You rarely need it: typing a character no built-in set
has switches to the font on its own.
Deckboy v0.89.5
Text mode's picture ink now draws each cell in the clip's own colour. It was
quantising every cell to a 16-entry EGA palette whose only mid-tones are grey,
so ordinary footage — which is mostly desaturated mid-tones — came out grey and
white nearly everywhere. Measured on one held frame: 19.5% of the source's lit
pixels are desaturated, and 89.3% of the glyphs drawn from it were. It is 20.1%
now, against the source's 19.5%.
The quantised look is what the palette ink is for, and that is unchanged.
Deckboy v0.89.4
Fold the inspector's sections down to reach the effects and everything stayed
where it belonged. EFFECTS and TEXT MODE are drawn after the per-cue-kind
sections, continuing from wherever those finished — and a collapsed section was
not counted, so those two drew on top of the folded headers. With everything
folded they landed at the top of the panel, over the lot.
SECTION playback|metadata|geometry|key|effects|timer|tone|text over the
control protocol folds a section from a surface.
Deckboy v0.89.3
The controls work on a clip
Every control in the TEXT MODE section — columns, glyph set, shuffle, ink, the
glitch amounts, the custom glyphs and the phrases — was reachable only on a
video synth cue. On a clip, a still or a camera carrying the TEXT MODE effect,
which is the ordinary way to use it, the rows drew and did nothing. They work on
any cue with the effect now.
CHAOS
A new row in the TEXT MODE section. At 0 each cell draws the glyph its
brightness asks for, so the picture reads. At 1 it draws any glyph in the set,
so the whole alphabet appears at once and the grid becomes texture. In between
is the interesting part.
This is how you get every mark in music & sparkle on screen: ranked strictly
by ink, a flat area of picture picks one mark and the rest never appear. Turn
chaos up and they all do. It works on custom glyph sets too, picking within what
you typed rather than the table behind it, and it is hashed from the cell
position rather than the frame, so it is texture rather than flicker.
Fewer columns means bigger cells, which is where the notes become legible rather
than reading as dots.
Also
ASCII INK, ASCII SET, ASCII SHUFFLE, ASCII COLS <n> and
ASCII CHAOS <0..1> over the control protocol, so a surface can drive the
character grid — and the same commands work on any cue carrying the effect.
Deckboy v0.89.2
Columns, glyph set and ink in the TEXT MODE section now change what you see. On
a cue carrying the effect the picture is built from the effect's four
parameters, and those three rows were writing to the cue's own synth settings
instead — so the numbers moved and the frame did not, and the ink row could read
"green" over a full-colour picture. The rows read and write the parameters now,
which is also how you reach music & sparkle: cycle the GLYPHS row.
One mapping between the four parameters and the grid, shared by the renderer and
the inspector, so the two cannot drift apart again.
Deckboy v0.89.1
The character grid now reaches every edge of the raster. Cell edges land on
proportional boundaries instead of a fixed cell size, so the last column and the
last row finish exactly on the frame edge at any column count and any output
size — no strip left along the right or the bottom. The glitch marks follow the
same grid, so they stay in their cells out to the edge.
Deckboy v0.89.0
A marks alphabet for text mode
A new glyph set, music & sparkle: dots, rings, an arc, a tilde, plusses,
crosses, diamonds, a star, and quarter, quaver, beamed and double-beamed notes.
Cycle to it on the GLYPHS row like any other set, or reach it from the text mode
effect's glyph-set parameter — so an LFO can sweep the alphabet along with
everything else.
The custom glyph field understands pasted characters now. Type or paste a row of
marks and they map to the ones that are drawn, with several spellings each — a
star arrives as a different character depending where it was copied from, and
they all land on the star. Custom sets can mix ordinary letters and marks
freely, and the existing SHUFFLE seed re-maps which mark carries which
brightness, so one set gives many different hands.
Devices you named, kept
A deck remembers the audio interface you chose, even when it is not there yet.
Start the machine before the rack is powered on and the deck says so — "not
found, on default" — instead of quietly forgetting what you asked for. When the
interface appears, the deck moves back to it on its own; when one is unplugged
mid-show, the deck moves to the system default rather than going silent.
The same for a control surface. The chosen MIDI port is saved with the show now,
so it survives a restart, and a port that is not present is reported by name
rather than replaced with whichever one happened to be first. Unplug the surface
and Deckboy says so; plug it back in and it reconnects itself.
deckboy --devices prints the audio devices, displays, MIDI ports and
render drivers this machine offers, with each device's real rate and channel
count and each display's real refresh and scaling — and it spells the names the
way a show file needs them.
Audio cues can be trimmed
In and out points on an audio cue, in the inspector and over the control
protocol, the same as a clip. A music bed can start eight bars in.
Steadier
- The show file is written beside itself and renamed into place, so it is never
half-written on disk. - Opening a show no longer writes to it.
- Streams and capture inputs get GEOMETRY, KEY and EFFECTS in the inspector.
- A DeckLink input has a full inspector, and names the card it is watching.
- Timecode readouts roll over correctly at the minute.
IN,OUTandMIDIover the control protocol report what they actually
did, with the reason when they could not.
Deckboy v0.88.0
Text mode is an effect, so it works on anything
The character grid is no longer part of the video synth. Put TEXT MODE on
a clip, a capture card, a camera, a browser cue or a still, and it draws as
characters — the same grid, the same glyph sets, the same phosphors, on
whatever the cue happens to be.
A cue carrying the effect gets its own TEXT MODE section in the inspector with
the full set of controls: columns, glyph set, shuffle, ink, custom glyphs,
phrases and phrase hold. Four of them — columns, corruption, glyph set and ink
— are also effect parameters, so the ones worth grabbing mid-set sit on faders
and can take an LFO.
Its amount is a mix, not a switch. At 1.0 the grid replaces the picture;
part way it sits over the original like a screen door, which is where a lot of
the best-looking settings turn out to be.
Bring your own characters and your own words
Text mode takes a custom alphabet and a list of phrases:
- custom glyphs — the characters the picture is built from, darkest first.
Two characters gives binary rain; a word gives that word as texture;
box-drawing pieces read as a schematic. - phrases — words separated by
|, one showing at a time, landing
somewhere new each time it moves. phrase hold sets the dwell.
Over the wire as ASCII ON|OFF|TOGGLE, ASCII GLYPHS, ASCII PHRASES and
ASCII HOLD, with Companion actions for all of it.
The code source is a language now
It reads like code because it is. Name values, build on them, and end with what
the channels should be:
ox = sin(t)*0.55;
oy = cos(t*0.8)*0.4;
d = length(cx-ox, cy-oy);
glow = smoothstep(0.45, 0.0, d);
glow, glow*0.35, 1-glow*0.6
Named values are also faster: a distance used by three channels is computed
once rather than three times. A source with no semicolon in it is exactly the
one-line form, so everything already written keeps working and keeps meaning
the same thing.
Seven more functions — length, smoothstep, if, sign, exp, log,
atan — and the editor grew to match: Shift+Enter for a new line, the
field sized to the lines in it, your own names syntax-coloured as names, and
six worked examples that start from the statement form. The helper alongside
it is set in a bigger, brighter face and explains each function as you reach
for it.
VJ mode has a switch
Settings → System → SHOW FLOW, at the top: one deck and a playlist, or two
decks and a crossfader.
The creatures come out when you want them
The switch is three-state: off, when idle, or always. "When idle"
stays the default and keeps the chrome still during a show; "always" is for
anyone who would rather have them there regardless.
Steadier through long sessions
The video synth holds a flat memory footprint however long a cue stays live,
and runs at full frame rate at 4K. Text mode renders at 60fps on a 4K raster.
Deckboy v0.87.0
Two decks, a crossfader and a tempo; a source you write instead of load, with
a real editor and a friend to explain it; an LFO on every effect parameter; and
eight new effects, six of which come out of physics rather than out of another
plugin.
VJ mode
A toggle. Off, Deckboy is a cue deck and every existing show renders exactly as
it did, through the same code path. On, two decks run at once and a crossfader
decides what the audience sees.
The decks were never the missing piece. Project::decks has always been a
vector and each deck has always had its own engine, playlist and transport --
what was missing is that an output could only ever be fed by ONE of them. So
this uses the layering hook that was already there and folds a mix gain into
the opacity each deck layer already carried, which means a deck faded down or
mid cue-fade stays faded down.
Both decks fade on a dissolve, not just the incoming one: they are drawn over
black, so holding A at full until B covered it would be a wipe. Add and
multiply are ways of combining two pictures, so there the base stays at
full and only the incoming deck rides the fader. Verified by recording the
composite with deck A solid red under deck B solid blue -- dissolve walks
250/0/0 to 0/0/253 through 64/0/127, add gives magenta, multiply gives black.
Colours in neither clip, which is the proof they are combined and not switched.
Tap tempo averages the recent taps rather than taking the last interval:
nobody taps evenly, and one interval makes the tempo jump on every beat. Taps
more than two seconds apart start again, because that is a person restarting
and not a 25bpm track. Quantised takes hold until the next beat -- the
point of tempo in a video mixer is not that anything moves by itself, it is
that what the operator does lands ON the music. Measured at 60bpm: unquantised
takes fire in 0.04s, quantised ones wait between 0.16s and 0.81s depending on
where in the beat they were asked for.
It announces itself. A mode you can enter without noticing is a mode that
ruins a show, so there are two signals: a band across the program column that
exists only in VJ mode and carries the controls rather than just announcing
itself, and the whole window edged in a colour used nowhere else -- for the
glance across a room before anyone touches the machine. Both playlists are on
screen side by side, each headed with which side of the crossfader it is,
because two lists both saying PLAYLIST is how the wrong clip reaches an
audience.
The animation carries information rather than decorating. The bar drops in over
a third of a second so the layout settles instead of jumping; the badge and the
frame breathe on the beat, which doubles as a tempo readout you can see without
looking at the number; and the fader handle leans the way it is travelling and
trails a wake that fades as it settles.
VJ ON|OFF | MIX <0-1> | BLEND <dissolve|add|multiply> | TAP | BPM <n> | QUANTISE <on|off> | DECKS <a> <b> | STATUS over the wire, because a crossfader
is a fader and a fader is the one control nobody wants to reach for with a
mouse.
Five bugs found building it. Two in the mix itself, both invisible from
outside: renderTextureWithCueGeometry overwrote the caller's blend mode,
silently discarding add and multiply while dissolve appeared to work, and the
crossfader had to be applied on the GPU zero-copy path as well as the CPU
bridge. Three in the look of it, all found by screenshotting the thing rather
than reasoning about it:
- The window edge was drawn AFTER
SDL_RenderPresent-- painting every frame,
perfectly, onto a back buffer nothing ever showed. The signal designed to be
impossible to miss had never once appeared. - VJ mode pushed the program monitor right to make room for the A preview by
advancing the column's own x, so the timeline lanes and the entire transport
row moved right with it while keeping the full column width, and ran off the
edge. TAKE-adjacent controls, clipped away, in the mode where the second deck
is live. - The bar was authored at fixed widths totalling 670px and VJ mode leaves the
program column around 500, so TAP and the tempo -- the two controls you reach
for on the beat -- were the two that fell off the end. The controls squeeze
toward a floor now, the fader takes what is left, and labels that cannot
survive the squeeze say less instead of being cut in half.
And the crossfader's own readout was the same colour as its handle, so the
handle ate a digit whenever it passed under the number. It sits in a dark well
now, legible at every position.
A code source you can write during a show
A pattern type called Code: the picture is an expression, evaluated per
pixel, edited while it runs.
sin(x*12+t)*0.5+0.5, sin(y*9-t)*0.5+0.5, r
One expression, or three separated by commas for red, green and blue.
Variables are x y (0-1 across the frame), cx cy (-1..1 from the
centre), r (radius), a (angle) and t (seconds), with sin cos tan abs floor fract sqrt min max mod pow atan2 step clamp mix to build from.
Why not GLSL. Deckboy draws through SDL_Renderer, whose backend is D3D11,
D3D12, Metal or OpenGL depending on the machine, and SDL's own shader path
wants SPIR-V, DXIL or MSL -- already compiled. Accepting GLSL at runtime on
every platform would mean bundling a shader compiler, tens of megabytes and a
per-backend translation step, to run arithmetic that fits in a few hundred
lines. So it is evaluated on the CPU, which is viable for the same reason the
effect stack is: the frame splits across cores.
The expression is compiled ONCE into a flat instruction list, cached against
its own text, and the inner loop sees only the instructions -- never a syntax
tree, which would spend its time chasing pointers instead of drawing.
A compile error does not black the output. The cue keeps drawing what it
last drew and the error appears in the inspector. Someone editing live is
mid-keystroke most of the time, and a source that goes black on every
half-typed function is unusable on a stage.
The language has its own test suite, and it earned its keep immediately:
multi-argument functions did not compile, unary minus bound so loosely that
-3+5 came out as -8, and ^ was left-associative. Division by zero, mod by
zero and the square root of a negative are all bounded rather than producing
infinities or NaN, because an operator typing at speed will produce all three.
The Windows CI gates were passing without checking anything
Deckboy is a GUI-subsystem binary on Windows, and PowerShell does not wait for
those: & .\Deckboy.exe --smoke returns immediately, $LASTEXITCODE is never
set from it, and the step passes whatever the app actually did. The Windows
--self-check and --smoke steps had been doing this, so on that platform
they had been reporting success without ever reading a result.
The tell was there in every log: the app's output appears AFTER the step that
was supposed to have run it. It was found by a new packaging gate failing with
no exit code in its message at all -- an empty value, rather than a number.
All three now use Start-Process -Wait -PassThru and read the real exit code.
Linux and macOS were never affected; their binaries are console subsystem and
the shell waits.
The Windows packager also runs the STAGED copy now, not just the one in the
build directory. The build tree has every DLL the build machine happens to
have; the staged tree is what people download, and it had never been started
before being zipped.
Releases now build and publish themselves
The scripts to build an installer and a portable package for all three
platforms have existed for a long time. CI built exactly one of the six --
the macOS .dmg -- and attached it to nothing; the Windows zip was made by hand
on a developer machine, and Linux shipped nothing at all. The README promised
an installer and a portable build for every platform, and only the macOS half
of that had ever been true. v0.86.0 was tagged and never released.
Tagging now produces all six and publishes them: -windows-x64-setup.exe and
-windows-x64.zip, -macos-arm64.dmg and .zip, .AppImage and
-linux-x86_64.tar.gz.
Nothing is built in the publish step. Every file is downloaded from the job
that already tested it, so what reaches the release page is the same file that
passed --smoke -- and both new packaging jobs unpack their own output and run
the binary from inside it before uploading, because an installer nobody has run
is a guess. The release refuses to publish unless all six are present: a
half-empty release page looks like a release.
The notes come from this changelog's own section for the version being tagged,
so the release page and CHANGES.md cannot drift apart.
Packaging runs on every push to main, not only on tags. Only the publishing is
tag-gated -- so the packaging is exercised continuously rather than discovered
to be broken at the moment somebody wants to ship.
The effect chain tells you what it costs
A cue has always been capped at twelve effects, but a cap only bounds the
damage — a dozen cheap ones are free and four expensive ones at 4K are not, so
the count an operator can already see is the wrong number.
The EFFECTS section now shows what the chain actually costs per frame,
measured on that machine at that raster while the cue is live, against the
16.7ms a 60fps frame allows. Over budget, it says so. That is the difference
between "it is stuttering, why" and "this chain costs 47ms".
Measured rather than predicted, and only once it has run: a figure added up
from per-effect benchmarks would be a guess about somebody else's hardware,
which is exactly what the number is there to avoid.
Going over is not a failure, and it is worth knowing what it does. Audio is
the master clock: sound continues in real time and the picture slaves to it,
so you lose frames rather than sync.
The Compa...
Deckboy v0.85.0
Deckboy can now record what it puts to air, take audio in, and generate its own pictures and sound. Three capabilities it did not have — plus the work that turns the first one from a viewing copy into something an edit suite will accept.
Program recording
RECORD sits on the button bar in the OUTPUT group, pulses while armed, and shows the running file size. Its destination is its own setting, separate from the encode queue's.
The recording is its own standard. Raster and rate are set independently of the programme — RECFORMAT 1920x1080 59.94 off a 4K programme scales on the GPU before readback, so the recording moves a quarter of the bytes. Both default to following the input, because a recording should look like what went in unless somebody says otherwise. Rates are exact where broadcast says they are exact: 23.976 is 24000/1001, not 23.98.
Constant frame rate by construction. The file has to contain exactly rate × elapsed frames. An encoder stamps by arrival order at the declared rate, so delivering fewer frames than promised does not slow the file down — it shortens it. A twenty-second take became eight seconds and looked perfectly healthy until an editor opened it. The pacer counts what is owed, repeats the last picture to cover a gap, and if it cannot keep up it says so out loud, once a second, on the output health state and in the show log.
Timecode. Start at a value, at time of day, or at zero; drop-frame, non-drop, or auto. Auto is right by rate — DF only means anything at 29.97 and 59.94, where it skips two timecode numbers a minute (except every tenth) to keep the count against the wall clock. No picture is ever dropped. The flag is carried in the file as SMPTE intends.
Codecs a post house asked for. ProRes (LT, 422, HQ, 4444) and DNxHR (LB, SQ, HQ, HQX) alongside H.264 and HEVC, in the right container, at the right pixel format, with the right vendor tag.
Segmentation and safety. Roll to a new file every N minutes or N megabytes, with a 3.8 GB ceiling so a FAT32 card cannot silently truncate a take. On stop, a fragmented recording is remuxed into a normal MP4 — OBS's trade, and the right one: a power cut leaves a playable file, a clean stop leaves a tidy one.
It keeps up. The frame leaves the GPU through an asynchronous staging ring instead of a synchronous read that stalled the render thread for 21 ms a frame, and the control window stops taking vsync while an output is recording. Verified against a 4K programme: 2160p25, 2160p30, 2160p50, 2160p59.94, 1080p50, 1080p29.97 and ProRes HQ 1080p25 all frame-exact.
That fast path is D3D11, so macOS and Linux take the portable read. Measured rather than assumed — frame-exact at 1080p50, 1080p59.94 and 2160p25, and behind only at 4K above 30p, where the alarm fires. Record 4K at 25 or 30, or record 1080 off a 4K programme, and every platform is frame-exact.
Audio input
Microphone and line input, with device selection, gain, a clip indicator, mono folding and a settable recording bitrate. It routes to the programme, so it reaches the stream and the recording. Streaming and recording had been carrying silent audio on Windows entirely; that is fixed.
ASIO (Windows)
Vendored SDK, driver enumeration, and a real-time output callback with a ring buffer, so cue audio can reach an interface directly. A device whose sample rate does not match gets a conversion rather than a refusal. macOS and Linux continue through CoreAudio and ALSA.
The synth sources
- Tone generator — the audio counterpart of a test pattern, with diagnostic displays.
- Chip voices — 2A03 and FDS as one SYNTH section, playable from parameters rather than presets, and playable live over MIDI or the computer keyboard.
- Video synth — oscillators, mirrors and feedback, a glitch stack, hardware palettes, CRT, a text mode with the full 95-glyph ASCII set, and sprite sets from
data/spriteswith rotation, flip, jitter and chaos per tile. Import a sheet or a folder of sprites through a picker.
Timer
Custom colours, six chimes, a rest that is actually silent, an event logo, and message placeholders.
Also
- HAP conversion is offered where it would actually pay, with the real numbers.
- Datamosh gains an EXTREME recipe and per-cue encoder overrides.
- First launch always shows the green branded wordmark; splashes rotate per theme.
- The macOS and Linux builds are fixed. ASIO's stub file was only compiled on Windows, so those two linked against declarations with no definitions; and the recording readback sat inside the decoder's namespace, which a build without in-process decode does not compile at all. Both structural, both fixed — all three platforms now build and smoke clean in CI.
Downloads
- Windows —
Deckboy-0.85.0-windows-x64.zip, portable; unzip and run. - macOS (Apple Silicon) —
Deckboy-0.85.0-macos-arm64.dmg, or the.zipof the same bundle. Built and smoke-tested in CI. - Linux — build from source; see the README. There is no portable bundle yet.