Changelog
What's new
Release notes for Zestial.
Changelog
All notable changes to Zestial. This file is the source of truth for the
/changelog page on zestial.app and stays in sync with GitHub releases.
The format is loosely based on Keep a Changelog; dates are ISO-8601.
1.0.0 - 2026-08-24
The first public release: everything added since 0.1.0, plus a pre-launch
audit and the fortnight of repair it produced.
Added
Effects and overlays
-
42 effects (30 scene + 12 post), up from 15. A shipped GLSL library of
seventeen scene effects and eight post passes, authored in the same custom-effect
format users write in.
-
Three new overlays. Lyrics (LRC), a Now Playing card, and a Waveform Strip.
-
Real prerendered thumbnails for every effect, overlay and Starter Look.
Every gallery tile is a genuine frame of the real effect, not an illustration.
-
Categorised Add gallery so 42 effects stay findable.
The editor
-
Undo / redo (⌘Z / ⌘⇧Z). Slider drags collapse into one step; applying a
Starter Look is one step, not fifteen.
-
Duplicate a layer or overlay (⌘D), bindings and all.
-
Per-layer opacity and blend modes (Normal / Add / Screen). Opacity is
bindable, so a layer can fade in on the bass.
-
Layer in/out points with fades. Bars for the intro, ribbons for the drop.
-
Save your own presets and Looks. A local library that works signed out and
offline; Zestial Cloud syncs it across devices.
-
Project palette. New layers seed their colours from a chosen set.
-
Per-track input gain, so a quietly-mastered track no longer makes the
visualizer look broken.
-
Surprise me. Roll a complete, audio-reactive composition in one click.
-
Relink memory. Reopening a project on the same device finds its audio
again instead of asking every time. Zestial remembers where the files sit,
not the files themselves, and nothing about them leaves your machine. Firefox
and Safari lack the browser API this needs and still ask on each open.
Export
-
Choose what to render: one track or the whole playlist, and an in/out range
within it. Effects with trails get a warm-up run so a mid-song in-point doesn’t
open on an empty accumulator.
-
Named platform presets. Reels/TikTok, YouTube, Square, Spotify Canvas.
-
Still frames. Save the current frame as a PNG at project resolution.
-
Every browser that can export video exports it with sound. Safari 16.4
through 18.7 can encode the picture but not the audio, and Zestial now brings
its own AAC encoder rather than handing those browsers a silent film — or, as
it actually did, promising one and then refusing the render outright.
Everywhere
-
A phone shell built for phones. Bottom sheets, a real player, swipe to
delete, and every keyboard-only action given a touch route.
-
Prism Z, the brand mark, now used everywhere: favicons, app chrome, touch
icon, social cards.
-
A demo track, so the app does something the moment you open it.
-
Installable, and works offline. The shell is precached; local-first is
now literally true.
-
Portrait 9:16 canvases, so Reels, TikTok and Spotify Canvas dimensions work
end to end.
-
Zestial Cloud (optional, opt-in, entirely separate from the editor).
Passwordless sign-in, synced projects and presets, unlisted share links, render
history. The editor stays free, un-watermarked and fully usable without an
account.
-
Share links. Send someone a composition and they can open it. Audio is never
included. They bring their own track.
-
Deep links from the site into the app, which now arrive with audio loaded.
-
Export and delete your Cloud data from the Cloud panel.
-
Delete a project from Cloud. Rows in My projects gain a delete control,
behind a confirmation. Your local copy is untouched.
-
Use another account. Switching accounts no longer means finding Sign out
underneath your project list, which under a subscription was off the bottom
of the panel.
-
Marketing site at zestial.app and the single-origin deploy serving the app
under /app.
Changed
-
The local-first promise is stated precisely. Your audio never leaves your browser — it is decoded, analysed, rendered and exported on your own machine, and nothing in the cloud ever receives a track. Earlier copy shortened that to “no upload, no account”, which was written before Zestial Cloud existed and was too broad once it did: sync, share links and accounts are real, and they are opt-in. The editor is complete without any of them.
-
Motion means the same thing at every frame rate. Trails, decay and
beat-driven kicks were measured per frame rather than per second, so the same
composition ran twice as fast on a 120 Hz display as in a 60 fps export. They
are time-based now: what you preview is what renders.
-
Prices read
$4 USDand$36 USD, with a note that some regions are chargedin local currency at checkout. Polar converts at checkout, and an unqualified
dollar sign made that look like a surprise rather than a conversion.
-
Filenames also match on a normalised form now, so a re-encode or a re-tag
still lines up with the track a project is asking for.
-
Gallery cards no longer run their live preview on hover when the system asks
for reduced motion. The tile beneath it is a real frame of the same effect, so
almost nothing is lost.
-
Site copy edited throughout.
Fixed
A pre-launch audit read the codebase end to end and found 5 high, 23 medium and
around 30 low-severity defects. Almost all of them are repaired below. They are
grouped by what they cost you rather than by where they lived.
Losing work
-
Applying a Starter Look deleted your custom shaders. It replaced the whole
custom-effect set rather than the layers, and no undo brought them back.
-
Autosave could drop the last second and a half of work — the debounce had
no path for a tab that closes. It now flushes on the way out.
-
One unreadable file in a batch rejected the whole batch. Drop ten tracks
with one bad file and you got zero tracks.
-
Undo could resurrect a removed track without its audio, leaving a playlist
entry that pointed at nothing.
-
The restore prompt could overwrite a project a share link had just opened.
Export
-
Dismissing the save dialog reported “Export complete” and discarded a
finished render with no way to get it back. It now says what happened and
offers the file again.
-
A playlist export rendered untouched tracks with no beat data, so every
beat and beat-phase binding sat perfectly still through them — after a preview
that looked right.
-
The screen is now genuinely held awake during a render. The dialog had been
promising it; nothing was implementing it.
-
Square (1:1) projects exported as 1920×1080 instead of a square video.
-
Multi-track export estimates were understated. The dialog sized the render
from the current track while rendering the whole playlist.
-
The done panel named a file that did not match the one saved.
-
A multi-track export applied one track’s input gain to all of them.
How things render
-
Six post effects were authored against pixels and handed a 1×1 resolution.
Scanlines collapsed into whole-screen tints, oil paint sampled the entire frame
as one brush, and halftone and anamorphic flare skipped aspect correction on
anything but a square. All six get the real output size now — and VHS Tape and
Subspace Resonance no longer alias into a two-pixel comb at 1080p.
-
Spectrum-driven custom post effects sampled a frozen first frame forever.
-
The spectrum assumed every track was 48 kHz, so 44.1 kHz material was
mapped about 9% sharp — most of a semitone, across the whole display.
-
Editing any layer tore down and rebuilt the entire post chain, reallocating
bloom’s render targets on every slider tick.
-
**Moving the window to a display of a different density, or zooming the
browser, left post effects sampling a stale frame.**
-
A lost graphics context left a permanently black stage. It now explains
itself and restores, and cancels an in-flight export rather than stalling.
-
Browsers without WebGL2 got a React stack trace instead of an explanation.
Playback
-
Fullscreen kept playing but stopped listening. The end of a track did
nothing there: no auto-advance, no loop, and a play button that had become a
no-op until you left fullscreen.
-
**Switching tracks mid-analysis could stamp the previous track’s tempo onto the
new one**, so beat-synced layers ran to a grid from a different song.
-
A backgrounded tab stopped advancing the playlist — a track ended, nothing
followed it, and you came back to silence.
-
Per-track input gain ratcheted to silence or to full instead of settling
where you put it.
-
Pressing play after the last track ran out restarts it. It used to start
the audio at its final sample, so the button appeared dead.
-
Removing the track that was playing left the transport showing the old one.
The editor
-
Parameter sliders can be grabbed anywhere they are drawn. The invisible
control was a third the height of the row it sat in, so presses landed in a
dead band, and the browser answered the drag with a “no drop” cursor.
-
The meter beside a bound parameter shows what the binding actually does. It
had its own private copy of the mapping maths, and the two had drifted apart.
-
Bar-beat ticks appear for a freshly loaded track. Tempo was read once while
detection was still running, so the answer was zero and nothing recomputed it.
-
The whole editor can be driven from a keyboard. Dialogs take focus when
they open and hand it back when they close, layers can be selected without a
mouse, and controls that appear on hover now also appear on focus.
-
Fullscreen scrub-drag no longer leaves listeners behind when a gesture is
interrupted, which had left stray pointer movement seeking the track.
-
Coming back from checkout flips the header to CLOUD on its own. It waited
for the window to regain focus, which loses a race with the payment webhook
often enough to matter.
Writing custom effects
-
**An imported effect whose id collided with a built-in silently added the
built-in instead.**
-
**The shader validator reported everything as valid when the graphics context
was lost** — the one moment it most needed to fail.
-
A number at or above 1e21 in a node graph produced invalid GLSL, and the
error pointed at a line the author never wrote.
-
Custom-effect documentation described a uniform that does not exist and omitted
three that do.
0.1.0 - 2026-06-21
The first public-readiness milestone. The core loop (drop audio → build a reactive composition → preview → export MP4) is complete and hardened.
Added
- Session autosave & restore. Your in-progress work is snapshotted locally and offered back after an accidental reload, with audio relinked on the spot.
- Export error surfacing. Encoder and muxer failures now show a clear message instead of a silent stall, and the UI returns to a usable state.
- Real export capability detection. Browsers that can genuinely encode H.264/AAC are ungated (Chromium today, Safari 26+), with a friendly notice on the rest.
- Feedback loops. A tempo-analysis indicator, and an inline “showing last good — fix errors” badge in the custom-effect editor.
- Licensing & shipping basics. PolyForm Noncommercial license (your exported videos are yours to use commercially), version + metadata, social cards.
Fixed
- A global unhandled-rejection safety net so background async failures surface as a toast instead of vanishing.