Releases: fgilde/videola
Release list
Videola 0.9.1 — sound you can fix by name, twelve titles, eight looks
Videola 0.9.1
Sound that can be fixed by name, twelve ready-made titles, eight looks, and a written plan.
Sound
- Bass and Treble — shelves, which is what "more bass" and "less hiss" actually mean:
everything past a corner rather than a hill around a frequency. The equaliser beside them is a
peaking band and cannot do it. - Hum filter — a narrow notch at the mains frequency, 50 Hz or 60. Its range stops at 70 Hz on
purpose: a filter that could be dragged up into the voice would be a low cut with a misleading name.
At the default sharpness the notch is about two hertz wide, so it takes the hum and leaves the note
beside it — which is exactly what a low cut set high enough to catch hum cannot do.
Each is checked against a real graph on both sides: the far side moves, the near side does not. A shelf
that moved both would be a gain.
Titles and looks
- Twelve ready-made titles in the Text panel — plain, lower third, headline, breaking news, neon,
quotation, caption, outlined, credit line, sticker, upright hook, note. One press replaces the whole
style, and a test refuses a move or a colour the renderer cannot draw, because both fall back
silently and would look like a preset that does nothing. - Eight looks in the effect shelf — Vintage, Cinema, Summer, Night, Film noir, Dream, Comic, VHS.
Twenty-three effects are unusable by somebody who does not know what "posterize" means; a look is the
same effects arranged by somebody who does. One press, one step of undo.
Editing
- Slow push (Ken Burns) on a clip: the move a photograph cannot make on its own, written as
keyframes rather than a mode, so it can be dragged, retimed or deleted afterwards. - Fade in and out: four keys on the opacity for the two clips that have nothing to dissolve with,
the first and the last. - Dragging a clip in the picture moves it by the distance dragged — it used to be thrown out of the
frame, because the whole travel since the pointer went down was applied to a transform that already
carried most of it. Four pointer moves, four times as far; a real drag reports dozens.
The site
videola.app is served from its own root now. The base was still /videola/ from the github.io days,
which is why the page came up without its stylesheet and every link pointed one directory too deep.
The plan
There is a written plan of record in the documentation now, in both languages: what comes next, in what
order, and what is deliberately not planned. Including the rule this project holds itself to — nothing
ships here that we do not own, which is why every template is a generator.
Videola 0.9.0 — titles that move, and menus you can find things in
Videola 0.9.0
Titles that move, six menus instead of one pile, and an export dialogue that explains what it is
offering.
Titles
- The movement of a title is something you can build. A title could arrive rising, growing or
fading, and no control anywhere set it: the presets lived in the renderer and in the shipped
templates, so a template could show a move nobody could make, change or take away. The Text
panel now carries five arrivals, five departures, a pulse, and the seconds each takes — plus the
look: colour, weight, alignment, type size, text width and height in the frame. - The seconds field appears only once a move is chosen. A duration for an animation that does not
happen is a question about nothing. - The bug underneath it, which was ours from 0.8.0: two of the shipped templates asked for moves
the renderer does not draw, and the style reader takes the default rather than the word it does not
recognise — so a mistyped move looked exactly like no move at all. Fixed, and pinned: a test walks
every shipped template and refuses a move or a loop the renderer does not implement, naming the clip
and the word.
Where things are
- Six menus: File, Edit, Insert, Project, View, Help. Everything used to sit behind one overflow
disclosure, and a list of fifteen unrelated actions is a list nobody reads twice. These are the
names every editor uses, so "where is import" has the answer somebody already knows. Undo, redo,
export and save stay on the bar. - On a phone the same six become groups inside one disclosure — same tree, one level deeper. On a
tablet the wordmark and the export button come off the bar, because six German titles are 450 px
and the bar was pushing the page wider than an 834 px window.
Handing the cut on
- File ▸ Hand off replaces three menu lines reading "Export EDL", "Export FCPXML", "Export
Premiere XML". Each file is a card now: what it is in a sentence, which program opens it, and what
it does not carry. Somebody who does not already know what an EDL is was choosing between three
XML-ish names by their initials. - The two exports that belong to the project rather than the cut — the subtitles and the sound for
Audiola — moved into the same dialogue. A card is switched off with its reason where this project
has nothing to write: "This project has no subtitles".
Known
The effect shelf comes up without its tiles in the headless application harness — sixteen tiles, no
pictures, no error. It draws normally in a real browser. One hypothesis (the picture being read off
the WebGL canvas) was tested and ruled out this release, with the check kept; the cause is still open.
Videola 0.8.0 — templates that work before you have footage, and a cut Premiere opens
Videola 0.8.0
Templates that work before you have footage, a cut that opens in Premiere, and four defects in the
interface that were worth naming.
Templates
- Every placeholder is optional, media included. A media placeholder used to be required, so no
template could be used until something had been imported — backwards on the first run, and wrong
anyway for half of what templates are for. A lower third, an end card, a quote card: those are
wanted over an edit that already exists, and their own stand-in shot is in the way. A template used
with nothing chosen comes back as its graphics. - Insert as tracks. The last step of the wizard has a second answer beside Create project: add
the same result to the project already open, as tracks of its own. One press of undo takes the whole
thing, however many tracks it brought, and it bakes at the open project's own size so nothing
arrives cropped. - Two new templates, both made of generators end to end — Three cards, three lines arriving one
after another over a field that stays, for a chapter break or the front of a talk; and Upright hook,
a 9:16 card with a disc behind the middle word, the first shipped template built on theshape
generator. Fifteen ship now, and these two are the first that are the whole card with nothing
imported. - An unanswered placeholder no longer leaves a bare track behind.
Handing the cut on
- Final Cut Pro 7 XML (
xmeml5), which is the file Premiere Pro imports as a real sequence. A
different format from FCPXML despite the similar name, and the one that actually opens: Resolve →
FCPXML, Premiere → this. Counted in whole frames, files declared once and referenced after that, and
project_handOffoffers it to an agent as well.
The interface
- The two menu entries that could not be used at all — Insert and Change shape — are real
groups now. They were selects inside a menu that closes on any click, so the click that opened the
dropdown closed the menu underneath it. - The about dialogue was a bulleted list of links. It has a header band, a version chip, links as
rows a finger can hit, and a footer. - The timeline context menu stays inside the window: flipped above the pointer where there is no
room below, pulled back where it overhangs, capped in height with its own scroll where it is taller
than the window. - The version in the about dialogue is the real one. It was two releases behind, because it was a
constant somebody had to remember; it is compiled in from the release manifest now. - Gain reduction on every compressor and limiter — a bar and a number, read from the live node,
which is the one thing about a compressor that cannot be derived from its settings.
Self-hosting
Three new ways in, all checked against the code rather than written from memory: a Proxmox helper
script that builds an unprivileged LXC and waits for the container to answer, an Unraid Community
Applications template, and an Umbrel app. All three carry a mandatory token, because the server
refuses to listen on a reachable address without one. They install a new server package —
videola-server-0.8.0.tar.gz, the editor, the API, MCP and the CLI in one tarball that needs nothing
but Node 22.
Also
- Find the cuts inside a clip and split it there.
- Give a clip a name; make a track taller or shorter.
Videola 0.7.0 — motion blur that is motion, noise reduction that is spectral, and surround
Motion blur that is really motion, noise reduction that is really spectral, surround, and the two
things an effect chain was missing: a way out and a bypass.
New
- Motion blur, over eight real instants of the exposure. A clip carries a shutter — 0 off, 0.5 a
180-degree shutter, 1 the whole frame — and the renderer draws it once per instant of that window.
Each instant asks the core where the clip reads from then and where it stands then, and the
decoder for that picture: a clip travelling across the frame smears along its own path, including a
curved one, and material moving inside the shot smears because the samples land on different source
frames. The samples are averaged additively into a target of the clip's own and composited once — a
run ofoveroperations would weigh the last one most and be a trail rather than a smear. In the
preview and in the file, at the same eight instants. What it cannot invent is temporal detail the
material does not have, and the effects chapter
says exactly where that line is. - Spectral noise reduction. The thing the two cut filters cannot do: separate a voice from noise
sharing its band. Noise is steady and speech is not, so the floor is learned from the pauses —
the quietest fifth of the clip, averaged bin by bin — and every window is then turned down per bin by
however much of it is floor. Own radix-2 FFT, forty lines, checked against a discrete transform
written out by hand. Measured: the noise inside the tone's own band drops by more than 9 dB while
the tone stays within 2 dB. - Surround. A project is laid out over stereo or 5.1, chosen on the master strip, and every track
has a position in that field: pan left to right, rear front to back, and an LFE send that is a band
(a low-pass at the 120 Hz the specification names) rather than a place. Pairwise constant-power
placement between adjacent speakers, so a sweep keeps its loudness; a stereo bed keeps its width and
a bed panned inwards reaches the centre speaker. A 5.1 mix the browser cannot encode is delivered in
stereo rather than in silence, with the placement kept. - Effects can be removed and bypassed. Until now one could be added and never taken off again, and
Effect.enabledsat in the model since the first schema with no command able to set it — the
renderer and the audio graph had honoured the flag the whole time.effect.remove,
effect.setEnabled, and a bypass switch and a bin in every effect row. - A clip can be switched off. It keeps its place, its length and everything else; nothing draws it
and nothing plays it. Dimmed and hatched on the timeline. That is how two takes are compared. - Keyboard. Undo had no key at all. Now:
Ctrl+Z,Ctrl+Shift+Z(andCtrl+Y),Ctrl+S,
Ctrl+O,Ctrl+Eon the window;Sto cut at the playhead,Ctrl+A,Ctrl+Don the timeline;
+,-,0andHome/Endfor the view. - The view follows a running transport, paging ahead to keep the playhead on screen and never
scrolling itself while the transport stands still. - Rubber-band selection over empty timeline, selecting what it touches and giving it back when
dragged shut. - Three effects: mosaic (a face, a plate, a screen — a blur strong enough to hide one leaves its
shape readable), directional blur, and glow that spreads what is bright and keeps what is not. - Insert in the project menu: a lower third, a full-frame title, a credits card, a shape or a
countdown, laid down at the playhead. There was no way to add a title at all before. - Shapes and the countdown are drawn. Both were in the model from the first milestone and nothing
drew them. - Four more templates — quotation, before-and-after, interview, countdown — thirteen in five
categories. - Export presets: 1080p, 4K, 720p, upright 1080 × 1920, square 1080. Sizes and frame rates only,
never a bitrate. - One curve shape on every parameter of a clip. Written off once as needing the keys to line up,
which it does not: an easing belongs to a key, not to a moment.
Fixed
- The installer said the wrong company. Windows takes the publisher from the bundle identifier
when nothing says otherwise, and that identifier wascom.cargonerds.videola. It is
org.gilde.videolanow, withpublisher: gilde.orgstated outright. Changing an identifier moves
where the operating system files the application, which is why this belongs before a release. - The download button swallowed its own label on hover. It went from
#5b8cffto#7d9bffunder
white type — about 2.3:1. The fill stays put now and the light around the button answers the
pointer. True of every hero button as well, so the fix is in the theme. - A field inside the timeline lost its keys: typing an m into a marker's name dropped a marker
per letter. - A selection kept the ids of clips an undo had removed, so every action that asks "is there a
selection" said yes and then found nothing to do. project_handOffwith an unknown format now names the two it has instead of quietly writing
FCPXML.
What this round cost
Written down because the mistakes are the useful part:
- A stationary test signal cannot be told from noise. The first denoise test used a sustained
sine, so the floor was the tone and everything came out 18 dB down. Real material pauses; the
tests do now too. - An overlap-add synthesis divides by nearly zero at the ends. Without a window of padding the
ends of every clip came out eighteen times too loud — invisible to every ratio measured inside
the file, obvious to a peak reading. - Mixing three amplitudes at once loses 3 dB. The first surround panner did, and the check that
measures power at fifteen positions caught it. - A generator has no decoder. A smeared title came out black until its samples took the painted
picture; the export harness found it. paintShapefell through to its last branch and drew an ellipse for a shape name it did not
know. A mutation test found that one.
Not there yet
Layouts beyond 5.1, true peak (dBTP), gain-reduction metering, stabilisation and tracking. Signing keys
for iOS, Android, macOS and the Tauri updater are the user's to provide — without them a release carries
no updater configuration, and the path itself is built and tested.
Checked
231 Rust tests in the core across 18 suites, 1860 JS tests, and five browser harnesses: 436 GPU pixel
checks, 75 export checks through ffprobe and ffmpeg, 423 application checks, 251 layout checks and the
server bundle. Every claim above is one of them.
Videola 0.6.0 — it updates itself, and it goes vertical
Updates on both sides, a download page that knows which system you are on, a shape change for
vertical cuts, freeze frames, and the browser build as an installable, offline-capable application.
New
- Downloads that find you. The download page works
out which system is asking, offers that build first and lists the others with their sizes. The list
is read from the latest release at load time, because a page that spelled out file names would go
on offering last spring's build months later. Sizes in the reader's own notation — 7,3 MB on the
German page, 7.3 on the English one. Android and iOS say that they are missing from a release and
why: they exist only where a signing key does. - The browser build updates itself. A tab left open for a week used to run whatever was current
when it was opened. A service worker now notices a new build: the new worker installs beside the
running one and waits, the editor offers a reload, and the swap happens on that reload. Nothing is
exchanged under a running session —clients.claim()is deliberately absent. - And it installs, and works offline. A manifest makes it an application on the desktop or the
home screen; the same worker serves it without a network. Two caching rules and both follow from
how Vite names what it builds: a name carrying a content hash can never change, so it comes from
the cache; everything else, the document above all, goes to the network first, because that is the
request which tells the browser a new build exists. - A real update dialogue on the desktop. Which version, a button, and a bar that reports how far
the download has got — or goes indeterminate where the host reports bytes with no total, because a
bar that invented a number would lie about how long this takes. It cannot be dismissed mid-download,
and a failure says the version in hand keeps working. (The updater still needs a signing key on the
release; without one a build carries no updater configuration at all.) - Change shape. Portrait, square or 4:5 in one press, with every clip scaled to cover the new
frame in the same step. The factor applies to what the author already chose, and placement scales by
the axis it is measured on, so a lower third stays a lower third instead of ending up 1080 pixels
from the middle of a taller frame. - Freeze frames. Two cuts and a rate of zero — the core has always meant a frame hold by that.
The piece after the freeze is slipped back by the held duration, because the held piece consumed no
source: without that, a freeze puts a jump at its own end. - A transition on every cut. A slideshow is fifty pictures and forty-nine dissolves. One choice
dresses every cut where two clips actually meet, and its first entry takes them all away again. - Overshoot in the curve field. The field now reaches a third past the unit square at each end,
which is what makes a bounce authorable: a handle above where the travel arrives sends the value
past its destination and back. Two firmer lines mark where the travel sets off and arrives, and the
unit square is kept square so the dashed diagonal is still a diagonal. - One easing on the whole track, paste attributes from the clip on the clipboard, an about
dialogue, a keyboard shortcut sheet whose every row is a key the editor really answers, a
splash screen for the desktop build, and a layout setting for the case detection gets wrong.
Two decisions worth reading
A service worker may cache files and nothing else. The first version cached every same-origin GET
and swept up the test harness's own control requests. Under a virtual clock each of those is a pause,
and a cache write hung on every one meant the editor never got a turn to draw — nought tiles of
thirteen. The narrow rule is the correct one anyway: an API answer served from an offline cache is a
wrong answer served confidently. And sw.js must never be cached long, because the worker that would
fetch a newer one is the stale one.
A wide window is not always a desktop. Three layout checks had been failing on CI for days, and
the cause was not a layout fault: the shell asks (any-pointer: fine), which is the only honest
question a page can ask about what is being pointed with, and a headless runner with no mouse answers
no. A 1440 px window was being laid out as a tablet, and the checks were reporting, correctly, that
the picture was 216 px tall — a true statement about a layout nobody meant to check. Every run now
pins the layout it names, and the application carries the choice behind that as a remembered setting.
What this build is
Desktop installers for Windows, Linux and macOS, plus a Docker image carrying the editor, the HTTP API
and the MCP server. The macOS disk image is unsigned unless an Apple certificate was configured, so
Gatekeeper will refuse it until you allow it by hand. Android and iOS assets exist only for releases
built with their signing keys.
FFmpeg is not bundled: the export runs on the browser engine's own encoders, and the server cannot
render.
Videola 0.5.0 — lookup tables, curves, locks, and the picture as a control
Lookup tables, a curve editor for keyframe easing, a lock that means something, and the geometry of
a shot on the shot itself. Five releases in, the picture is finally the largest thing in the window.
New
.cubelookup tables. A table is a library asset under the hash of its own bytes, with a
media kind of its own — so it packs into a.videolawith no line added to the writer, and
nothing about it crossespostMessage. Proven at pixels through the preview, the decoded MP4 and
a frame ffmpeg read back out of the written file.- A curve editor. Bezier handles were kept and honoured but could not be dragged. The field
opens beside the picked key's interpolation, over the tracks; the line it plots comes out of
segment_shapein the Rust core — the very functioninterpolateapplies to move the picture —
so a drawn curve and an animated one cannot disagree. Fifteen columns of measured difference at
the same instant say it reached the frame. - Geometry on the picture. Select a clip and a box appears on the frame: a handle on every
corner, one to turn by, and a drag inside it to move the shot. The corners come from the same
matrix the compositor hands the GPU — checked against each other over translation, uneven scale,
rotation, an off-centre anchor and every combination with a crop — so the handles sit on the
picture rather than near it. A corner holds the corner across from it still; a rotation is the
angle between the handle and the pointer, so a pointer that leaves the window and comes back
lands where it is. The whole drag is one undo step. track.lockedis enforced. It sat in the model from the first commit and meant nothing. Now
one gate in front of the whole command dispatch refuses every edit to a locked track's clips —
their trims, speed, transforms, effects and keyframes, the track's own chain, and the track
itself. The mixer, the name and the flags stay reachable, which is how it is unlocked again. An
insert or overwrite ripples every track, so a single locked track anywhere refuses it outright:
skipping it would move the picture out from under its own sound.- Beats as markers. A rise against a threshold that moves with the music, so the quiet half of
a track yields the same hits as the loud half. Markers and not cuts — the beat is a suggestion to
cut against, and a hundred cuts nobody asked for are a hundred clips to take back one at a time. - A mixing desk you open. The desk and the instruments both cost the picture about a fifth of
the window each, and both stood there whether or not anyone was using them: with both open the
preview was left sixty pixels of seven hundred and its canvas hung out over the toolbar. Both are
now switches on the transport, folded away until asked for.
Two decisions worth reading
A lock is one gate, not twenty checks. Honouring it inside each handler would mean a lock that
half the commands respect, and the next command anyone adds would be a hole nobody notices. So it is
answered once, in front of the dispatch, by a function that names the tracks a command would change
clips on. Five deliberate mutations of that function were tried; all five died.
A LUT is a medium. The alternative was a new field on the project and a new entry in the
container writer. Under the hash of its own bytes in the library it is already everything the
.videola format knows how to carry, already deduplicated, and already something the export worker
can read without being handed it.
Also
The screenshots in the documentation now come out of the application harness and are cropped to the
page rather than to the window — headless Chrome lays the editor out in a window taller than the
page, and every desktop picture in the guide had carried a hundred and fifty pixels of black under
it. pnpm --filter videola-docs shots is the whole of that step.
What this build is
Desktop installers for Windows, Linux and macOS, plus a Docker image carrying the editor, the HTTP
API and the MCP server. The macOS DMG is unsigned unless an Apple certificate is configured, so
Gatekeeper will block it. Android and iOS assets are absent whenever their signing keys are.
FFmpeg is not bundled: the export runs on the browser's own encoders, and the server cannot render.
Videola 0.4.0 — colour, captions, classical editing
Colour grading with scopes, subtitles, classical three-point editing, adjustment layers, and audio
finishing. Four releases in, the editor now covers most of what a day's work needs.
New
- Colour. Waveform, vectorscope and histogram, read off the preview canvas that was already
readable. Curves as a first-class parameter type, keyframable point by point — the "parameter
that is not a float" hurdle named back in M3 is now cleared. Lift, gamma and gain wheels. - Subtitles. SRT and WebVTT in and out, on a caption track of its own — a separate track kind,
because only that can answer which clips are subtitles rather than lower thirds. A millisecond is
exactly 705,600 flicks, so the round trip is lossless by arithmetic; it is checked byte for byte
through the parser and again through the real Rust core. - Three-point editing. Mark in and out in a medium, then insert or overwrite at the playhead.
Insert opens the gap on every track — a gap on picture alone would put sound out of step for
the rest of the film. Each is one command, so an insert across three tracks and a dozen clips is
one press of undo. - Adjustment tracks. They existed in the model and drew nothing at all. Their clips' effects now
run over everything below them, through the same channel a compound clip already used — zero
compositor changes. - Audio finishing. Live meters per bus, normalise to a loudness target (and measure again rather
than trust the arithmetic), ducking that writes keyframes you can see and edit, silence detection
that cuts the pauses out. - J/K/L shuttle, markers with colours and notes, preview resolution, an effect browser whose
every tile is that effect's own shader over the current frame, nine templates in five categories
with baked preview cards, and a keyframe lane on the timeline.
Two decisions worth reading
A mask is an effect, not a model field. Everything a mask needs already existed for effects, so
masks cost zero lines of interface. Two masks in one chain intersect, and a mask is keyframable, both
for free.
track.locked is deliberately not enforced. A lock is honoured nowhere in the core; making one
command respect it would have made that command the sole authority on what a lock means.
How it is checked
488 Rust tests, 1513 JavaScript tests, and five harnesses needing only an installed Chrome:
366 pixel checks against a real WebGL2 driver, 41 export checks that hand the written file to
ffprobe and ffmpeg, 200 against real browser layout, 252 driving the built application on desktop,
tablet and phone viewports, and one driving the built server bundles over real stdio and HTTP.
Installers
Windows NSIS, Linux deb and AppImage, macOS DMG — all unsigned. Android and iOS are wired but
skipped without signing keys.
Videola 0.3.0 — nesting, keyframed transforms, agents that can see
Compound clips, keyframable transforms, agents that can see their own work, and a Docker image that
serves more than static files.
New in this release
- Compound clips. Fold a selection into one clip. That the picture does not change is proven
six ways, each against the render before the fold: the whole frame buffer at tolerance zero,
the draw list at sixteen instants, the source mapping in Rust at 1/60 s steps, the recorded GPU
batch replayed, and the audio render sample for sample — plus a counterweight so "the compound
drew nothing" cannot pass. - Keyframable transforms. A clip's position, scale, rotation and anchor animate over time,
resolved in the Rust core like every effect parameter, so the preview and the export cannot
disagree.Clip::keyframeshad existed since the first schema with nothing reading it. - Track and project effect chains have an address in the model, and the master volume has a
command. Nothing renders or sounds them yet — that is the next step, and it is now unblocked. project_getFrameandproject_getAudioPeaks. An agent can look at what it just cut. The
still comes out of the same wasm core, draw list and WebGL2 compositor the editor draws with, in
headless Chrome, so it cannot show something the editor would not.- Autosave. Project state into OPFS every 30 s — not a
.videola, because the media are
already there under their content hash. The banner offers, never takes. - Docker that does something. One Node process serves the editor, the HTTP API, the MCP server
and the CLI. nginx is gone. It refuses to start on a public address withoutVIDEOLA_TOKENand
says why. - A CLI:
videola apply | describe | validate | schema. - Auto-update wired for the desktop builds, injected by CI only when both signing key halves
exist.
Fixed along the way
Media imported through the API got a library entry with no duration, no dimensions and no channel
count — which silently dropped every such clip from the draw list and from the audio. A get_frame
on a server-imported video would have shown the background and reported success. Both hosts now
build the entry through the same function.
What is still missing
No masks, motion paths, motion blur or LUT import. No EQ or compressor. No noise reduction, ducking
or beat detection. A compound clip is flattened rather than isolated, so opacity, effects and blend
apply per nested clip. The magnetic timeline is deliberately absent — the useful half is ripple
delete and trim, and the rest would change an overlap rule that transitions, layering and roll/slide
all depend on. FFmpeg is not bundled.
How it is checked
385 Rust tests, 1022 JavaScript tests, and five harnesses needing only an installed Chrome: 198
pixel checks against a real WebGL2 driver, 35 export checks that hand the written file to ffprobe
and ffmpeg, 52 against real browser layout, 167 driving the built application on desktop, tablet
and phone viewports, and one driving the built server bundles over real stdio and HTTP.
The application harness is not a CI gate: it depends on font metrics, core count and devtools
timing. Loosening what it asserts would be worth less than the assertions.
Installers
Windows NSIS, Linux deb and AppImage, macOS DMG — all unsigned. Android and iOS are wired but
skipped without signing keys.
Videola 0.2.0 — it edits
Videola edits now. Drop a video in, cut it on the timeline, add effects and keyframes, mix the
audio, and export a file a player opens — on a desktop, a tablet or a phone.
What is in this release
- Editing — ripple delete and trim, roll, slip, slide, multi-selection, groups, clipboard,
markers, snapping, zoom from one frame to a whole project. One Pointer Events path for mouse,
pen and finger; a whole drag is one undo step. - Playback — WebCodecs decoding into a WebGL2 compositor, the audio clock leading, a
frame-accurate transport. - Effects and transitions — brightness, contrast, saturation, colour temperature, vignette,
blur, sharpen, chroma key; cross dissolve, wipe, slide, zoom, dip-to-colour; a text generator
with in/out/loop animation. Every parameter keyframable, interpolated in the Rust core so the
preview and the export read the same values. - Audio — mixer with volume, pan, mute and solo, fades as scheduled automation, waveforms,
EBU R128 loudness checked against the Tech 3341 conformance cases. - Export — MP4 with H.264 and AAC, or WebM with VP9 and Opus. A browser that can encode a
format's picture but not its sound writes a silent file rather than failing. - Templates —
.videolatis the same container as.videolaplus one entry, so a template is
a project with questions attached. Four ship, none carrying footage. - An HTTP API and an MCP server — the whole 37-command catalogue, generated from the Rust enum.
What is not in it
No masks, motion paths, motion blur or LUT import. No nesting of compound clips, no magnetic
timeline, no autosave recovery. No EQ or compressor: the browser has the DSP, the model has nowhere
to persist a track-level effect yet. FFmpeg is not bundled — the export uses the browser's own
encoders.
How it is checked
Four harnesses that need no Playwright, only an installed Chrome: 192 pixel checks against a real
WebGL2 driver, an export verified with ffprobe and ffmpeg by decoding the file it wrote, 52 checks
against real browser layout, and 167 that drive the built application with a real video file on
desktop, tablet and phone viewports. Plus 348 Rust and 923 JavaScript tests.
The last of those four is not a CI gate: it depends on font metrics, core count and devtools
timing, and loosening what it asserts would be worth less than the assertions. It is green on a
developer machine.
Installers
Windows NSIS, Linux deb and AppImage, macOS DMG. All unsigned — macOS Gatekeeper will block the DMG
until signing certificates are configured. Android and iOS are wired but skipped without keys.