Skip to content

3.1.0 — Timeline, Clip Deck, accidental-close protection, Electron 43

Choose a tag to compare

@CaYatur CaYatur released this 02 Sep 23:46
· 250 commits to main since this release

Two performance surfaces that share one clock and one quantiser, accidental-close protection, and a move to Electron 43.

Timeline

Tracks holding clips, and automation lanes that write into the same targets the modulation engine already exposes — anything modulatable is now also automatable.

  • Transport with play, pause, stop, a loop region, and scrubbing that updates the scene immediately, including while paused.
  • A zoomable canvas ruler reading in seconds and in bars. Grid density follows the zoom, so it never turns into noise zoomed out or empty space zoomed in, and zooming holds the instant under the cursor still.
  • Clips move by dragging and trim from their edges, snapping to bar, beat, half, quarter or frame. Hold Alt to suspend snapping mid-drag.
  • A keyframe editor reusing the modulation engine's curve set, so a curve named the same behaves the same in both places. Automation curves are drawn by sampling per pixel rather than joining keyframes with straight lines, which would show a shape the engine does not play.
  • Named markers, jumpable, importable from an LRC or SRT file through the existing lyrics parser.

Seconds are the single source of truth and bars are derived from a tempo map. Storing both would eventually let them disagree with no way to tell which was right. Conversion accumulates across tempo changes, because multiplying by one constant BPM shifts everything after the first change and the error only shows during a show.

Clip Deck

  • A sparse grid: empty slots are not stored, so an 8×8 deck holding one clip writes one record rather than sixty-four.
  • Beat-quantised launching from one frame up to four bars, with a countdown on the armed slot. Launching exactly on a grid line waits for the next one — otherwise hitting the beat squarely makes quantisation look random.
  • A cut is a true cut, with the fade forced to zero rather than left to the drawing side. A fade reuses the existing 18 transitions.
  • Launching a column fires the whole row on one frame, aligned to the longest quantisation in that row so nothing staggers.
  • Follow actions — stop, loop, next, random in column, go to a slot — which chain, with a per-frame fire cap so a chain of near-zero-duration clips reports an overrun instead of pinning the machine.
  • A performance view with nothing but the deck, transport and blackout: large targets, high contrast, every slot reachable from the keyboard.
  • Deck activity records back onto the timeline as tracks, turning an improvised set into an editable one.

Sources are picked from the scenes, templates and colour presets that actually exist, by name. Nothing has to be typed from memory.

Accidental-close protection

Two switches in Settings, both off by default.

With protection on, a visualizer window that closes unexpectedly reopens at once. Recovery keys off intent, so closing from the panel, from the remote or with Esc is never undone. A second switch locks Esc, leaving the panel's Close button and Ctrl+Alt+Shift+Esc as the ways out — a visualizer that cannot be closed would be worse than one that closes by accident. Recovery gives up after four attempts in ten seconds and says so, rather than pinning the machine on a window that dies as fast as it is recreated.

Electron 43.5.1

33.4.11 reached end of life in April 2025 and shipped Chromium 130; this release runs Chromium 150 and Node 24. It is also the floor for the Spout and Syphon senders planned for v3.1.1, which need Electron 40 or newer.

Two things had to change with it. Electron 35 altered the console-message event so that level became a string where it had been an integer, and both self-test listeners compared level >= 2 — which a string silently fails. The self-test would have stopped collecting renderer errors and passed by not looking. Separately, the self-test reported failures but still exited 0, because app.quit() does not consult Node's exit code, so no automation could ever have gated on a smoke failure. Both are fixed and both directions are measured.

Determinism

Offline export is this project's visual regression net, and it only works because every time-dependent source derives from the draw clock. The timeline obeys the same rule: automation evaluated at a time is the same value however the playhead reached it, and the exporter derives time from the frame index alone. The live path uses a separate wall-clock anchor so that several visualizer windows compute the same playhead in closed form — without a message per frame, and without drift between them.

Fixes found while building this

  • The visualizer is fullscreen and always-on-top, so it covers the admin panel, and Chromium stops requestAnimationFrame in a covered window. The timeline and deck would have stopped advancing exactly while a show was running.
  • Taking the transport object and calling play() on it did not refresh the clock anchor, so every programmatic caller — including the new MIDI and OSC transport actions — would have failed silently.
  • The add-track and add-marker buttons did nothing, with no warning: the model was rebuilt every frame, so edits went onto an object that had already been discarded.
  • Firing a slot wrote the transition setting and left it there, so one Cut slot disabled the user's scene transitions permanently.
  • A slot with a name but no source would arm, count down, fire and change nothing.
  • The English-translation gate only scanned what was in the DOM, so any panel that defaults to off could ship untranslated without anyone noticing.
  • The timeline could not be used at all: the button that creates a clip was drawn only when a clip was already selected, so on an empty track there was nothing to select and the first clip could never be made. Clip tracks now carry their own + Clip button, double-clicking empty space adds one, and an empty timeline spells out the three steps instead of showing a bare ruler.
  • The loop could have its start set from the playhead but not its end, and a loop whose end is not past its start disables itself in silence.
  • followPlayhead was in the configuration but never implemented, so playing let the playhead run off the edge and the timeline looked frozen.

Not in this release

Written down rather than dropped quietly: multiple decks, the timeline arming deck slots, multi-select on the timeline canvas, editing a full tempo map through the interface, rendered clip thumbnails, and applying video, image or shader slots — those have to target a layer, and doing it without naming one would overwrite whatever is there.

Verification

703 unit tests, the GPU self-test, and the packaged build's own self-test all pass. The self-test asserts, in the real application: seven accidental-close scenarios, automation reaching the visualizer draw loop, the show clock anchor arriving there, the panel transport advancing, a quantised launch landing exactly on a bar line, an armed slot firing, the panel buttons persisting their result to the configuration, the source pickers listing real scenes by name, and the user's transition setting surviving a deck slot.

Please check on real hardware: the white flash on opening a visualizer. electron/electron#45774 is closed as not planned and the workaround survived the Electron upgrade in the self-test, but nothing here can verify it across your actual monitors.