Changelog

This page lists the notable changes to Kinetic, @wildflowerjs/kinetic, with the newest release first. The format follows Keep a Changelog.

0.2.0 2026-11-13

Added

The finish can change over a piece #

Any part of the finish except the grain's fps can be a track, { at, to, ease }, which Kinetic reads at each frame's time. A track given in place of a field is that field's amount. The overlays the page shows follow the tracks on every seek. See A finish that changes.

A diffuse field in the finish #

The finish takes diffuse, { amount, radius }, with a radius of 16 px by default. It blurs the whole frame and mixes the blur over it, the look of a soft-focus filter. Like the bloom and the aberration, it is in the rendered file only.

moves() builds a camera from a list of moves #

moves(list) builds a camera from a list of moves, each with an ease, lag, overshoot, kick, swing, drift, or steady-rate dolly. Moves add together, so they blend, and each one ends exactly on its values. look(eye, at) points the camera at a point, and a move can end on one. See moves(list, options) and look(eye, at).

cameraAt() gives the camera's numbers at a time #

cameraAt(t, tracks, options) returns the camera's numbers at time t, { x, y, z, pan, tilt, roll, zoom }, with any shake added. depth()'s camera option takes these numbers.

depth() can draw a layer finer #

depth(z, { detail, camera }) draws a layer 1 to 8 times finer. It is for a layer the camera magnifies while it is turned, which Chrome otherwise draws at its CSS size and stretches. camera takes the numbers from cameraAt(), and the style depth() returns gains zoom. A detail outside 1 to 8, or one given without the camera, is KN-100.

land() and arrival() time a move by when it arrives #

land(t, at, duration, ease) is ramp() started early enough that the move arrives, within 3% of its end, at at, such as a beat. arrival(ease, duration) gives the seconds from a move's start until it arrives. For a spring ease, that time is the same at any duration.

timeOf() reads beats and frames #

timeOf('beat 9') is the time of beat 9, the same as tempo().at(9), and timeOf('frame 170') is that frame's time at the composition's rate. data-start reads neither form. tempo().frame(n) gives beat n's time on its nearest frame, and onFrame(t) gives the time of the frame nearest t.

kinetic.25d.js adds solids() #

A second file, kinetic.25d.js, loaded after kinetic.wf.js, adds solids(target, options). It draws boxes, cages, and faces of your own from 3D coordinates into an SVG element as paths, which it culls, clips at a near plane, sorts far to near, and shades by one light through a ramp of colors, with a rim and glows. The same values draw the same SVG whatever was drawn before. Loaded without kinetic.wf.js, the file prints KN-100 and does nothing. See Solids in 2.5D.

config({ encode }) sets the encoder #

config({ encode: { codec, bitrate, audioCodec, audioBitrate } }) sets the encoder for every render() that does not give its own, including the preview bar's Render button. A wrong field or value is KN-100.

data-viewmode shows an element in the video or on the page alone #

data-viewmode="render" shows an element in the video alone, and data-viewmode="live" shows one only while the page plays. viewmode() and the store's viewmode are 'render' while check() or render() runs or a render captures, and 'live' otherwise.

The preview bar shows a frame as the render draws it #

On a piece with a finish or a shutter, the preview bar has an As Rendered button. It pauses the piece and lays the frame over the stage as the render draws it, with the shutter and the finish. A frame step or a scrub draws the new frame, and playing, a render, or a check turns the button off. It needs what a render needs.

A notice before a render inside another site's frame #

In a page inside another site's frame, which may block the render's download, the preview bar's Render button first says so, with Render anyway and Close. config({ frameNotice: false }) turns the notice off. See The preview bar.

A warning for a second composition root #

Development builds warn when a page has a second composition root beside the first (KN-155). Kinetic drives the first one alone. See the diagnostics list.

The skill's check.cjs checks the layout #

Under layout:, check.cjs lists text outside the safe area at rest, text cut off at the frame's edge, text over other text, and text under its contrast floor, measured against what is behind it. --no-layout skips the pass.

check.cjs measures a piece's pace #

check.cjs takes --pace calm, lively, or bold. It measures the share of frames near still, the snaps a minute, the longest stretch near still, the range of brightness, the share in smooth ramps, and the tones in use. It compares each with the expression dial's target and marks each miss.

check.cjs finds seams and jumps #

--seam reports the change from the last frame to frame 0, and --jumps reports every pair of frames that changes far more than the frames around it. --diff a,b reports the change between two frames, and --isolate hides each selector in turn over that pair and prints how much of the change it takes away. --strip tiles the frames --frames saves into one numbered strip.png.

check.cjs takes a view #

check.cjs takes --view render, live, or both, the view the whole check runs in.

The skill's guide has directions for lively and bold pieces #

The guide gains expression directions for lively and bold pieces, with the dial's targets, the laws a reel follows, and two directions, Flat Field and Flythrough. It also gains patterns for cutting to a track and for solids in 2.5D, and the Plus Jakarta Sans and Lora pairing. Its new rules cover moves timed by when they arrive, a move on every beat, and a whole number of dashes on a circle.

Fixed

An embed page with sound keeps its speaker button #

Under embed, a page with sound keeps Kinetic's speaker button, the only sound control an embed has. See Sound in the preview and Embedding.

Under embed, elements added later are hidden too #

Under embed, an element the page adds beside the composition after it loads is hidden, as the elements there at load are. See Embedding.

The fit scales a root sized as a share of its container #

With the fit on, a root that the CSS sizes as a share of its container is scaled by its laid-out width, and fills the space as a root sized in pixels does. See The config options.

The guide's lesson questions turn their choices off after the right answer #

A lesson question built from the skill's guide turns its choices off once the right answer is picked. Before, a wrong pick made after the right one showed its text while the explanation played and turned Next off. See A question that explains a wrong answer.

Changed

render()'s default video bitrate is 16 Mbps #

render()'s default video bitrate is 16 Mbps, up from 8 Mbps, which keeps the detail of grain, glows, and soft gradients. Pass bitrate to render(), or set it in config({ encode }), for another rate. See render(options).

The preview bar's Render button uses the page's settings #

The preview bar's Render button renders with the shutter, the finish, and the encoder set in config({ shutter, finish, encode }). Once the render is done, the bar reads "render complete", with the file's name, its frames, and the time it took, until the next thing done on the bar.

The fit follows the window's height as well #

With the preview bar or config({ fit: true }), the stage also fits the height of the window above the bar, so a portrait stage shows whole. In a frame, the page around it sizes the stage as before.

camera() throws when its tracks are a function #

camera() given a function for its tracks throws KN-100. A camera built with moves() is called for its numbers first, as in camera(t, CAM(t)).

config() reports the new settings #

config() reports frameNotice and encode with the other settings.

check.cjs checks the render view by default #

The skill's check.cjs checks the render view by default, with data-viewmode="render" elements shown and data-viewmode="live" elements hidden. --view live checks the page as it plays.

The guide sends a reel, a brand film, a sting, or a title to a direction #

For a showreel, a brand film, a sting, or a title at the lively or bold end of the dial, the skill's guide uses one of its directions in place of the default style.

The guide's script tags load 0.2 #

The script tags in the skill's guide load @wildflowerjs/kinetic@0.2.

0.1.2 2026-11-01

Added

text() takes any axis the outlines were baked with #

text() takes any axis the outlines were baked with, by its tag, such as Shantell Sans's BNCE, as a line or letter value beside wght and slnt. The outline baker offers every axis a font has. See Type with a weight per letter and the Outline baker.

Fixed

A page's own sound button starts the sound with one press #

config({ sound: true }) called from a click resumes the audio, so a page's own sound button starts the sound with one press. Kinetic's corner speaker no longer turns the sound off again. See Sound in the preview.

render() places a canvas with a border or padding #

render() draws a canvas that has a border or padding and object-fit in its place in Chrome 157 and later, which paints a canvas into its content box. Before, the canvas came out a few pixels off. See Canvases in a render.

Changed

captions() balances its lines #

captions() balances its lines. Between sentence ends and pauses, the words take the fewest lines maxWords and maxChars allow, about equal in length and broken after a comma near the middle. A short fragment is no longer left on a line of its own. See captions(t, words, options).

KN-131 says what to do on a page opened from disk #

On a page opened from disk, whose address starts with file:, KN-131 says that the page cannot fetch the files beside it, and to serve the folder or load the data as a script.

KN-170 waits for a run of slow seeks #

KN-170 prints when 3 of the last 30 seeks took longer than 8 ms, and gives the slowest and a typical seek time. Before, it printed on the first slow seek. See The check script.

KN-151 suggests a store's tick() #

KN-151's suggestion, and the skill's guide, put a lesson's frame-loop work on a store's tick(). A store's tick() runs in both modes, so it returns early unless the page is in interactive mode and holding. See Scenes.

The guide has more type pairings and palettes #

The skill's guide gains eight more type pairings and six more palettes (Tol Bright, Viridis Ordered, Neon Stage, Canopy, Lavender Clinic, and Solarized Light). It uses italics sparingly, where they suit the words. See Type pairings and Color palettes.

The guide has a pattern for a rate the learner controls #

The skill's guide gains a pattern for a rate the learner controls. The video sums the rate from the scripted settings, and the lesson advances it while a step holds, both from one rate function.

The guide's starter lesson lays out in normal flow #

The starter lesson lays its heading and line out in normal flow, so a heading that wraps no longer runs into the line. The guide also gains a pattern for ink plates out of register, boil()'s detail for large shapes, and a centered line that types in place. Its intake asks nothing when the request already gives enough to build.

0.1.1 2026-10-31

Fixed

render() of a DOM composition in Chrome 157 and later #

render() of a DOM composition works in Chrome 157 and later, where HTML-in-Canvas is on by default. Kinetic marks the composition root drawable for the render, which Chrome now requires of an element it draws, and takes the mark off after.

Changed

The guide's script tags load 0.1 #

The script tags in the skill's guide load @wildflowerjs/kinetic@0.1 and Trillium @0.2, so a page built from the guide picks up patch releases.

0.1.0 2026-10-31

The first release works with WildflowerJS 1.5.5 and later.