Release Notes
What changed in each Caddis release. Caddis downloads updates quietly in the background and then asks — you choose whether to restart straight away or have it applied the next time you quit.
1.1.0 — September 30, 2026
Added
Particles
- A particle system built from ordinary nodes. Point Emitter (Points menu) turns any shape or points — or nothing, for a single point at the comp centre — into births over time, with a lifespan and a starting velocity by direction and spread, outward from the source, from a vector field, or inherited from the points it fires from. Point Solver moves them: gravity, drag, max speed, substeps, any number of field nodes as forces, a comp-edge boundary (kill, bounce or wrap), and an option to point each particle along its motion. Particles are ordinary points carrying velocity, age and life, so Draw Points, Clone To Points, Draw Instances, Point Trail, Connect Points, Label Points, Select and Point Delete all work on them unchanged. Add a Point Emitter and Caddis builds the whole system for you. Scrub, jump or export anywhere and you get the same frames, and particles get real motion blur along their velocity.
- Rate and Burst. Rate is a steady flow per second; Burst fires a shot of N particles on a schedule — once at the start, every Nth frame, or whenever the source has something to fire from. A new Trigger node (Math menu) turns any signal into events, so Audio Source → Trigger → Burst gives one burst per beat instead of emitting through a drum's whole decay. The emitter shows how many particles it made each frame.
- Emitters that move and emitters that read images. A moving emitter hands its motion on to what it fires (Inherit Motion) and spreads births along the path it travelled during the frame, so a swept emitter leaves a continuous ribbon instead of beads. A Density input on Point Emitter and Point Scatter makes births follow a field — wire an Image Sample in and particles are born where the picture is bright.
- Particles collide with shapes. Wire any shape into Point Solver's Collide input — a rectangle, a logo, type through Text to Shape — and particles Bounce, Slide, Stick or die on contact, with Restitution, Friction and a Particle Size so they rest on a surface instead of touching it dead-on. Colliders can be animated.
- Flocking and per-particle forces. Separation, Alignment and Cohesion, with a Flock Range, give birds-and-fish behaviour. Force Scale lets each particle answer to forces by its own amount — write a temperature, set gravity upward, and hot particles rise while cool ones stall.
- Particles that die can spawn more. Point Solver's Died output carries the particles removed each frame, tagged with what killed them (age, the frame edge or a collision). Feed it into a second emitter set to Inherit for embers, impact bursts or a puff as each one ages out; Carry Over sets how much of the parent's momentum the sparks keep. Keep Dead For holds dead particles in place for a few frames so trails and connections fade out with them instead of vanishing.
- Size, colour and opacity over life. Point Attributes can drive its curve or colour ramp by each particle's Age, its Speed, or a stable per-particle Random; Shape Attributes gets Random per vertex.
@speedcan be picked anywhere an attribute is asked for, in pixels per second like every speed you type. Point Attributes and the Details panel show the live range of age and speed so you can set ranges without guessing. - Draw Points renders on the GPU, drawing hundreds of thousands of points in a few milliseconds with the same look. New Shape option — Circle, Square, or Sprite, which draws an image from a new Sprite input at every point — and a Blend option (Add or Screen) so sparks, fire and bokeh add light where they overlap. Draft and Preview quality now match Full instead of drawing small points larger and dimmer.
- Field Gradient turns a scalar field into a force pointing toward brighter values: a radial Gradient through it is an attractor with exactly the falloff you drew, an Image Sample pulls particles toward the bright parts of a picture. Points Field reports Density (how crowded) and Flow (the cloud's average velocity), and Field Math gains Normalize and Magnitude.
- A Confetti Burst recipe on the Recipe shelf: 180 pieces of paper in mixed shapes and colours, fired once, tumbling down through a noise field and fading out. Drop it in and restyle it, or take it apart to see how the particle nodes fit together.
Loops, For Each and Solvers
- Loop. A new Loop category in the node menu. Loop runs the nodes wired between its Loop In and Loop Out several times: in Feedback mode each pass works on the previous result (deform a shape twelve times, build recursive patterns); in Join mode every pass is kept and combined. For Each Point and For Each Path run once per point or path, handing each pass its element, position and index — a shape with its own drop shadow at every point of a grid, say. Loops can sit inside loops, each one drawn inside a frame in the node graph, and Clean Up lays them out.
- Solver: logic that carries from frame to frame. A Solver runs its nodes once per frame and hands the result to the next frame, so ordinary nodes inside it become rules that stick: delete slow particles for good, keep a colour set on impact. Every particle system is built on one, and the same Solver carries pictures too, for feedback tunnels, smears and paint that builds up. Scrubbing always shows exactly what playback would. A Solver's Start port sets what it starts from, and its values take any type — points, an image, a shape, a number.
- Switch picks one of its inputs by number, and only the picked branch is rendered — unlike Mix, the idle branch costs nothing. Inside a For Each it can give each point a different branch.
Layout
- Layout node. Divides a region into a grid of cells from numbers you can keyframe, so the grid reflows on the timeline. Columns and rows are edited on a proportion bar with a chip per track: drag a divider and two neighbours trade space, type a number for a share, add
pxto pin a track, or set a track to driven and wire a Measure in so a column fits its content. A track animating to zero takes its gutter with it so the neighbours close up cleanly (or set Gutters to Fixed for gaps that never move), and the E button makes gutter and padding drivable. Layouts nest: wire one Layout's cells into another and pick a Split Cell to divide just that cell, so a design whose columns split at different heights is a few small Layouts that read the way you would describe it. - Fit content to cells. Draw Instances and Clone To Points can fit what they draw to each cell's own size — Fit keeps the aspect, Stretch fills the box — so a logo or image lands correctly in every cell of an uneven grid and follows it as the tracks animate.
- Cell and Measure nodes. Cell gives you one cell of a Layout as plain values — width, height, size, centre and rectangle — and can split a layout's cells into the one you picked and the rest. Measure reports the size and centre of a shape, points, text or an image, with padding, so a plate can follow its text as the text changes. Selecting either shows the numbers live in Properties.
Timeline and layers
- Precompose. Select a block of layers and click the Precompose icon in the layer tools or press
CmdShiftC(CtrlShiftCon Windows and Linux) to move them into a new composition, replaced by one layer that nests it. Trim to layer duration makes the new comp only as long as the selected clips; turn it off to keep the full timing. Precompose checks the selection first and tells you if something would break — a matte or a parent on only one side of it, for example — rather than changing what you see. - Edit values right in Keys. The Keys view shows each parameter's value at the playhead and lets you type, scrub or toggle it there, with the same controls as Properties. Animated parameters get a key at the playhead. Keys and Graph each have a Node / Layer / Comp scope, so you can see just the selected node, the whole layer, or every animated parameter in the composition.
- Jump between keyframes with
JandK— previous and next keyframe within the scope you are looking at. - Extend a clip earlier. Drag a layer's in-point, or press
Alt[, back past where the layer started, all the way to frame 0, without moving its keyframes. - Scrub beyond the view. Drag the playhead or ruler past either edge of the timeline and it keeps panning through time, faster the farther out you go.
- Lock layers. A padlock next to the eye: a locked layer can't be selected, deleted, dragged or trimmed, and viewport clicks pass through it to whatever is behind. Click the eye, lock or motion-blur switch and drag down the column to set every layer you pass, in one undo step.
Shapes, 3D renders and effects
- Cryptomatte. A new Cryptomatte node (Source menu) reads the object, material and asset IDs that Redshift, Blender, Arnold, V-Ray and Karma write into an EXR and turns the objects you tick into a clean, correctly antialiased matte — including anything seen through glass and objects partly behind others. Invert gives you everything else. There's a search box for renders with thousands of objects, and dropping a Cryptomatte-only EXR builds the node for you. Use ZIP or PIZ compression when rendering: DWAA and DWAB are lossy, and Caddis warns you when it sees them.
- Twist. Shape Ops › Twist rotates a shape about an axis and flattens it, so an ellipse crosses into a figure eight. Keyframe Rotation for a seamless loop.
- Shape Deform can scale. A new Operation switch: Displace as before, or Scale, which multiplies the shape by the field — tapers, pinches, bulges and flares.
- Local fills travel with clones. A Draw Shape fill or stroke texture in Local mapping now moves, rotates and scales with each piece made by Clone To Points, including nested clones and shape palettes.
- A vertical 4K composition preset — 2160 × 3840, under Vertical / Social.
AI
- Nine newer models. Generate Image adds Seedream 5.0 Lite, GPT Image 2.5 and Nano Banana Pro; Generate Video adds MiniMax H3 Max (text and image), Kling v3 Pro, Grok Imagine 1.5, FLUX 3 and Seedance 2.5. Lists are newest first, so a new node starts on a current model. Older models are still there, and projects using them are untouched.
- Each model shows its own settings — quality, resolution, duration, audio — and the token cost above Generate updates as you change them. Generate Video's image inputs follow the model too, and it tells you when a model needs a starting frame.
- Generated video keeps its audio. When a model returns sound, an Audio Source is wired alongside the clip automatically. Audio is off by default, since some models charge extra.
- Hide AI features. A switch under Preferences › Appearance hides the Tokens display and the AI nodes from the menu, search and suggestions. AI nodes already in a project keep working.
Everything else
- Agent Access MCP can duplicate layers, slide clips along the timeline with their keys, and ease keyframes with the Graph Editor's presets or exact bezier handles — so an assistant can copy a finished title card and place it elsewhere with its animation intact.
- Search the node menu by operation. Typing "multiply", "normalize" or "smoothstep" lists entries like
Math · Multiplyand creates the node with that operation already set. - The Pan tool works everywhere. With Pan (
H) active, left-drag pans the node graph and all three timeline modes as well as the viewer. - The toolbars fit smaller screens. The top bar and timeline toolbar collapse into popovers as the window narrows, while the transport, playhead readout and Render stay put. Shortcuts for folded-away tools keep working. The window now has a 900 × 600 minimum.
- Inputs that take points or a shape say so with a half-green, half-blue port dot until something is connected.
- Point Relax, Connect Points and Points Field are much faster on big clouds — they only compare nearby points now, with exactly the same results.
Changed
- Lens Blur, rebuilt. A new renderer removes the grainy edges and ghost halos of the old one and never punches see-through holes in a solid image. Setting focus is now direct: click the eyedropper beside Focus Distance, then click the viewer. Near and Far hold your depth map's range — they fill in when you connect one, and Measure reads them again — so Focus Distance and Focus Range are percentages that mean the same thing whether your Z pass runs 0.1 to 0.4 or is in centimetres. Blur Size replaces Blur Strength and Max Radius: it is the biggest blur in the shot, in pixels. The zebra button stripes whatever is in focus, like a camera's zebra display, in the viewer only. Hard edges no longer leave a crisp outline against a blurred background, and in-focus areas stay sharp in the viewer while it previews large blurs quickly; Full Detail computes the viewer at full quality, and renders always do. Lens Quality and Denoise are retired. Projects made with the old controls convert when opened and look the same.
- Updates ask before they install. A new version still downloads in the background, but nothing installs until you say so. The Update ready pill opens a card naming the new version, linking its release notes, with Restart & Install or Not Now.
- Parenting a finished layer. Choosing a parent in the layer list now adds a clean Parent Transform just before the layer's Output, after its Draw nodes and effects, so the finished layer follows as one unit and your own transforms and animation stay as they were. No Parent detaches it where it is.
- New layers start empty, with just an Output node, so the first node you add wires straight to it. When a layer's Output is already in use, adding a shape, text or emitter still builds its full chain, left unconnected so your existing wiring stays put.
- Shortcut changes: Auto-key moves to
ShiftK(JandKnow step through keyframes); Copy Ease and Paste Ease move toCmdAltC/V(CmdShiftCis Precompose). - Timeline zoom stays centred on the playhead, whichever way you zoom and in all three modes.
Ffits the keys in Keys mode, as it already did in Graph. - Point Scatter's modes are named Fill / Path / Points (formerly Boundary and Vertices). Old projects convert on load.
- Time › Loop is now called Loop Time, to keep it apart from the new Loop.
- Variations on Draw Instances and Clone To Points follow the point. Palette picks, per-clone variations, Random time offsets and Clone Index's Random now stay with each point instead of reshuffling whenever a point is added or removed. If you picked variants by writing Point Attributes' Index target, switch it to Custom writing
shapeIndex.
Fixed
- EXRs holding a single pass now open. A lone depth or ambient-occlusion pass failed to load unless its channel was named R, G, B, A or Y. Any one-channel pass now opens as greyscale, and single passes appear in the Render Pass menu, so you can pull depth out of a multi-layer EXR. Cryptomatte ID channels are no longer offered as render passes.
- Point Trail's Fade keeps the trail's colour instead of turning every trail white.
- Point Advect and Point Trail are more dependable. They re-simulate when the frame rate or a field's source layer changes, render the same with motion blur on or off, stay within a memory budget, recover from a failed field read instead of freezing, and their selection outline follows the moving points. A single far-away point no longer blurs the field for everyone else.
- Point Delete, Select and Path Delete see their real input. Fed by an Image Sample mask, Point Delete deleted every point; after a simulation it could filter the wrong ones.
- Points scaled up through Point Attributes no longer clip at the composition edge.
- A keyframe after a Hold key shows its own value on its own frame, instead of one frame late — editing on that frame now works.
- Smoothed and deformed curves stay smooth. Shape Smooth no longer bulges between points, and Shape Deform no longer scallops outlines where its field changes quickly.
- Projects reopen where you left them — the selected layer, playhead and work area — and duplicating a composition keeps its node layout.
- Colour tokens stay on the gradient stop you dropped them on, and motion-path handles stay on the keyframe you selected when keys are added or removed.
- Quitting with unsaved changes quits (macOS) instead of leaving Caddis in the Dock with no window.
- Tab stays inside Composition Settings and moves from Width to Height.
- Tool buttons no longer keep a focus ring after you click them and use a shortcut.
- Smaller fixes: long model names are no longer clipped in the AI model menus; the AI badge is back on the video AI nodes; the E button appears on LUT 3D and Image Sequence Source; in-app help opens for Video Depth, Video Remove Background and Video Segment; Point Trail, Text and Text Animate keep all their ports when you press E.
1.0.6 — September 12, 2026
Added
- Nested compositions. Drag a composition from the Assets panel onto the timeline or viewer to use it inside another composition, like a precomp. It renders at its own size and length, plays from the layer's in-point (trim and slide it like a clip), and every edit inside the nested comp shows up wherever it is used. Under the hood it is a new Comp Source node, so a nested comp can also feed effects, mattes, and merges directly — drop a comp onto the node graph to add one to the layer you're editing. Use the Open button in Properties to jump into a nested comp. Compositions can't nest themselves — the picker and drag-drop rule those out.
- Nested compositions carry their audio. Comp Source has an Audio output like Audio Source; wire it into the Output's Audio input and the nested comp's sound plays during playback and RAM preview and lands in every audio-bearing export, following the layer's in-point, offset, and time mode. Dropping a comp with sound onto the timeline or viewer wires it for you; disconnect it to nest the picture alone. The nesting layer's Audio Gain, Pan, and Mute act as a bus over the whole nested mix.
- Audio levels are decibels everywhere. Audio Source's Level and the Output node's Audio Gain now use the same tapered dB fader as Audio Mix (
0.0 dBunchanged,−∞ dBsilent, double-click for 0 dB) instead of a bare 0–1 number. Existing projects convert on load with no change in sound. - Audio Mix node. A layer's Output takes one audio input; the new Audio Mix (Composite menu) is a mixer in front of it. Every connected Audio Source or Comp Source gets its own dB fader, there is a master, and an A/B crossfader blends whichever inputs you assign to each side (neutral at center, so it only acts when you move it). Faders taper like real ones, read
−6.0 dB/−∞ dB, and double-click back to 0 dB. Everything is keyframeable. - Keyframe handles are joined. In the Graph Editor a keyframe's in and out handles now move together: drag one and the other rotates to keep the curve smooth through the key, keeping its own reach. Hold
Alt/Optionwhile dragging to split them and shape a cusp; the split sticks to that keyframe (its handle dots become hollow squares) until you Join Handles from the right-click menu or set it back to Auto/Clamped. Works the same in the Speed graph. With several keyframes selected, dragging one handle moves the matching handle on every selected key by the same amount. - Copy Ease / Paste Ease. In the Dope Sheet and Graph Editor,
Cmd/CtrlShiftCcopies just the easing of the selected keyframes (interpolation, handle mode, bezier handles) andCmd/CtrlShiftVstamps it onto the selected keyframes, in frame order, repeating a copied pair across every selected pair. Values and timing stay put, so one hand-tuned ease can be applied across any number of curves. Both are in the keyframe right-click menu too.
Fixed
- Keyframes paste across nodes and layers. Pasting keyframes used to land only on the parameter they came from. Now they follow your selection: select another layer (or several) and the keys land on the same parameter of the same node type there — position from one Transform 2D onto another, a Blur radius onto another layer's Blur; select nodes in the node graph and every selected match receives them; select param rows in the Dope Sheet and those rows are the targets (a single copied param also pastes onto a row of the same value type, like position onto scale). With nothing else selected keys still paste in place at the playhead, relative timing between params is preserved, the pasted keys are selected afterwards, and a paste is a single undo step. Copying a whole param row now pastes at the playhead like any other keyframe copy, and parameters whose ids contain a colon (Audio Mix levels) copy correctly.
- Audio files can be dragged onto the timeline and node graph. Dropping a sound (or a video, sequence, or SVG) onto an existing layer's row used to do nothing; it now adds a new layer, as it does on empty track space. Dropping a sound onto the node graph adds an Audio Source node pointed at it.
- Drop Shadow now scales with a layer's transform. A shadow (or glow) on a parented or scaled-up layer kept its fixed comp-pixel size while the content grew, so the shadow looked too tight. It now scales its offset, blur, and spread with any Transform 2D above it. A new Scale with Transform toggle on Drop Shadow (on by default) turns this off for a shadow locked to absolute pixels. Glow, Inner Glow, and Inner Shadow now do the same, with the same toggle.
- Draw Shape taper fields are bounded. Taper Start and Taper End can't go below 0, and Start/End Length stay within 0–1, so a scrub can't produce an inside-out or overshooting stroke.
- Exporting over an existing file now asks. Since the export dialog started remembering your output path, re-exporting could silently overwrite the previous file. Now, when the remembered output name already exists, the Export dialog offers Overwrite, Keep both (auto-numbered:
render_2.mov,render_3.mov…), or Cancel before anything renders. Picking a path fresh through Browse still overwrites, as the system dialog confirms. - Tapered strokes now work on merged and multi-path shapes. Stroke taper was silently ignored on any shape with more than one path (e.g. after Merge Shapes) — strokes came out at uniform width. Taper now applies per path, so each stroke tapers on its own. A new Taper Scope option on Draw Shape switches to a single taper spanning the whole shape when you want a chain of paths to read as one continuous stroke. Tapering fully to zero now draws a crisp point instead of making the stroke vanish.
- PNG-sequence export progress no longer looks frozen. The progress bar sat on "Starting…" for the whole export and then jumped to done; it now advances per frame like video export does.
1.0.5 — August 26, 2026
A small follow-up to 1.0.4.
Fixed
- Agent Access preferences now lead with the endpoint URL. The MCP server works with any MCP client, not just Claude Code — the Agents tab now shows the universal endpoint URL first with a Copy URL button, and keeps the Claude Code one-liner as a labeled convenience.
1.0.4 — August 26, 2026
Caddis learns to work with AI agents. Agent Access — an opt-in local MCP server — lets Claude Code and other agents build node graphs, draw vector art, write shaders, and render frames to check their own work, live in your open project. Alongside it: the first round of Linux polish from tester feedback, and a rendering fix for scaled-up curved shapes.
Added
- Agent Access (MCP): AI agents can now work inside Caddis. An opt-in local MCP server (Preferences → Agents) lets Claude Code and other MCP clients build node graphs, set parameters, add keyframes, and render frames to check their own work — live in your open project, with every agent action as a single undo step. Agents can also draw vector art on Editable Shape nodes using SVG path syntax, edit gradient and curve ramps, write WGSL/GLSL for Shader nodes (compile-checked with line-mapped errors before anything is applied), reorder layers, auto-arrange the node canvas, manage compositions (create, rename, resize, duplicate — e.g. converting a 16:9 comp to 9:16), and import media into Assets-panel folders. Connections are local-only and the server is off by default.
Fixed
- Scaled-up curved shapes no longer get clipped in renders and exports. A pen-drawn or curved shape enlarged with a Transform 2D could come out with its curves cut flat by an invisible boundary — visible in exports and full renders while the viewport on Retina displays looked fine. The render canvas was sized from the shape's anchor points only, ignoring how far bezier curves bulge past them; it now accounts for the full curve.
- Opening the font menu no longer freezes Caddis on font-heavy systems. The font picker drew a live preview of every installed family at once, so on machines with thousands of fonts (common on Linux) opening it could stall the app long enough to look permanently frozen. It now draws only the rows in view — instant to open no matter how many fonts you have.
- The Export dialog remembers your format and output path. Both now persist per project, so re-exporting is one click instead of re-picking the same file every time the dialog opens.
- Nodes stay sharp while you drag a connection. The pulsing highlight on compatible ports could soften the whole node to a slight blur on some displays (reported on Linux); the pulse no longer blurs anything.
- Linux: the sign-in security notice now tells you the fix. If you run a keyring Caddis didn't detect (GNOME Keyring, KWallet, KeePassXC), the notice names the launch flag that lets Caddis use it.
1.0.3 — August 19, 2026
Caddis comes to Linux. Alongside it: a ground-up stability fix for Slitscan's Scan mode (steady exports, no wobble, no crashes), multi-layer EXR files that decode correctly — Blender's DWA compression included — and grow a Render Pass selector, and export presets that keep your filename's extension honest.
Added
- Caddis for Linux. The Mac and Windows builds are joined by a Linux release: an x86_64 AppImage that auto-updates just like the other platforms, plus a tarball for manual installs and package maintainers. Requires a Vulkan-capable GPU driver (Mesa or NVIDIA). Download it, make it executable (
chmod +x), and run. - Pick which render pass a multi-layer EXR shows. Image Source and Image Sequence nodes grow a Render Pass dropdown whenever the file carries more than one render pass — switch from the beauty pass to Emission, Ambient Occlusion, Depth, or any other pass Blender (or any renderer) wrote into the file.
Autokeeps the default pick and names the pass it resolves to. The dropdown only appears when there's actually something to choose — PNGs and single-pass EXRs are untouched.
Fixed
- Blender's DWA-compressed EXRs import correctly. MultiLayer EXRs saved with Blender's smallest-file codec (DWAA/DWAB) came in as silent black boxes; they now decode natively. Multi-pass files also pick the right layer: the Combined/beauty pass wins over whichever render pass happens to sort first alphabetically (it used to show Ambient Occlusion). And when an EXR genuinely can't be decoded, the import now reports an error instead of quietly showing black.
- No more hairline seam at Slitscan's moving scan line. In Scan mode a sub-pixel transparent edge could ride along the sample line — the layer behind showed through for less than a pixel — whenever the sweep ran over content extending past the frame. The frozen region's edge now stays fully opaque.
- Slitscan Scan mode notices edits on layers it watches through a Layer Source node. Editing a layer the scan sources — retyping its text, moving a keyframe, changing any parameter — now rebuilds the frozen history against the new content. Before, scanned rows kept showing the old content until something unrelated forced a rebuild, so cross-layer setups felt stale and unpredictable. Animated source layers still play back incrementally — only actual edits trigger the rebuild.
- Slitscan's Scan mode no longer slows down, wobbles, or crashes on long runs. The scan's frozen history now lives on its own composition-aligned canvas, so dragging the subject out of frame — the classic photocopier move — no longer throws the accumulation away every frame. Exports that used to grind to a halt after the first stretch of frames now render at a steady one-evaluation-per-frame pace, frozen slices hold perfectly still instead of wobbling, and the memory spikes that could crash the engine during long rebuilds are gone.
- Switching export presets updates the output filename's extension. Picking a destination under an H.264 preset and then switching to ProRes used to keep the stale
.mp4name, and the export failed immediately (ProRes can't live in an MP4 container). The chosen path now follows the preset —render.mp4becomesrender.movwhen you switch.
1.0.2 — August 18, 2026
Transforms learned to stack: chain Transform2D nodes and each one now works on top of the last instead of resetting it, with a viewport gizmo that tracks your edits instantly. Single frames export in one click. Around those: channel isolation in the viewer, Slitscan's photocopier Scan mode, fit-to-canvas, the Inter typeface built in, and a long list of fixes — including an engine crash caught by a Linux tester.
Added
- Save a single frame. A camera button in the viewer toolbar saves the current frame as a full-quality PNG into the project's
renders/folder (named after the comp and frame, never overwriting), with a confirmation showing where it landed. The Export dialog gains a matching Still Image → PNG — current frame option. No more one-frame "sequences". - Image sequences name themselves. A Filename field in the Export dialog names sequence files
<prefix>_00042.png— defaulting to the composition name — instead of the anonymousframe_00042.png. PNG and EXR both. - Channel isolation in the viewer. Shift+R / G / B / A shows that channel as grayscale, Shift+L shows luminance; the same key returns to RGB. Pure display — rendering, caches, and exports are untouched.
- Fit to canvas. Cmd+Alt+F fits the active layer to the composition (uniform scale + centering, one undo step); Cmd+Alt+Shift+F stretches. Transform edge handles now also snap to the canvas bounds while dragging.
- Slitscan Scan mode — the photocopier drag-smear. An animated scan head sweeps the frame, freezing each row as it passes; move the subject mid-sweep for the classic xerox smear. Fully deterministic — scrubbing and exporting always match the played-through result. The sample line is now a draggable viewport gizmo in FrozenLine mode, too.
- Published and linked nodes say so in the graph. Published nodes get an amber badge with their published name; linked nodes show what they link to.
- Reset a node to defaults. One button in the inspector restores every parameter to factory values (clearing their keyframes) as a single undo step — file paths, drawn shapes, text content, and publish wiring survive.
- The Inter typeface is built in. New text defaults to bundled Inter, so default type looks the same on every machine (previously it asked for Helvetica Neue, which only exists on macOS).
- The performance HUD shows your GPU — adapter, backend, driver — so bug reports can include it at a glance.
Changed
- Transforms now stack. Position in the default Pixels mode is an offset from where the content sits: a freshly added Transform2D changes nothing until you touch it, chained ones concatenate (rotation and scale pivot around the current content), and the Anchor Point is purely a pivot — moving it never shifts the content. Normalized and Pinned keep placing the anchor at an absolute spot — that's the point of pinning. Existing projects are updated on open and render identically.
Fixed
- Setting Draw Shape's stroke Miter Limit below 1 crashed the render engine — every comp went black until an app restart. It now clamps to 1 (the SVG minimum). Thanks to a Linux tester for the report.
- The transform gizmo got a truthfulness pass: the box and anchor crosshair land correctly on moved, off-center, and chained shapes; they track edits instantly instead of lagging one engine sync behind; and pausing playback no longer shows a stale box while the engine catches up.
- Spread on Inner Shadow, Inner Glow, and Drop Shadow actually spreads now — Photoshop-style growth through the blur falloff. The slider used to do nothing visible on hard-edged shapes.
- Alpha WebM files import with their transparency — including Caddis's own WebM Alpha exports, which used to come back opaque. The export dialog also now says where WebM transparency will actually display (browsers, OBS) and which players draw it on black.
- Importing one image no longer quietly becomes an image sequence — collapsing numbered files into a sequence is now the explicit choice for small selections.
- Odd-sided polygons rotate around their radius center instead of wobbling on their bounding-box center — a triangle spun over a reference circle stays concentric.
- Tile respects an upstream Transform2D (the grid follows the transformed size and position) and no longer leaves hairline transparent seams from half-pixel image placement.
- Blender "OpenEXR MultiLayer" and multi-part EXR files import correctly — the color pass is found automatically, whatever it's named. Images that can't be decoded at all now say why in the status console.
- Nulls and other zero-area layers get a grabbable minimum-size gizmo — previously they could only be moved by typing numbers.
- Keyframe editing: switching handles Free → Auto → Free no longer erases a custom ease; Reverse Keyframes mirrors the easing along with the timing; and scaling selected keys through a collision no longer strands or overwrites them.
- Fonts fail loudly instead of silently: a missing font warns and names its fallback, a corrupt system font is skipped instead of crashing the renderer, and Draw ASCII / Label Points fall back to a monospace font.
- The Shader node's validator now checks against your actual GPU at edit time, so machine-specific failures are flagged before render.
- Export completion messages report the real result — no more "Exported undefined frames to undefined", and EXR sequences get a proper "Exported N EXR frames" line instead of a bogus duration.
- Timeline view switches (Layers / Keys / Graph) keep your visible time range instead of scrolling back to the start.
- Shape Boolean cuts holes correctly when one shape sits fully inside the other.
- Dragging video files into the Assets panel works reliably on Linux (GNOME Files drags included), with status-console feedback when a drop can't be read.
- If Caddis can't initialize the GPU at launch, the error says what to do about it instead of reporting a generic engine crash.
1.0.1 — August 4, 2026
The first update since launch, and it's a big one: the geometry attribute system grew up. Per-point and per-vertex data now survives every shape operation, every attribute field is a proper picker with built-in @ properties, and new selection tools — the Select node, named groups, Apply To — turn rules into groups without writing a single expression. Around it: node-graph quality of life (bindable node shortcuts, forgiving search, drop-a-wire-to-create), and fixes for point simulations, File Cache playback, and a subtle compositing seam.
Added
- Attributes survive geometry ops. Nine shape operations silently destroyed per-vertex data — a color gradient through a Resample Shape rendered flat, Merge Shapes ate every array, clones came out unpainted. All nine now carry attributes under one registry of policies: continuous data (scale, opacity, color) blends, categorical data (glyphIndex, sourceIndex) snaps to the nearer parent, rotation blends the short way around — and no node gets to choose differently. Fixed: Resample Shape (blends at the arc position), Merge Shapes (true concatenation), Shape Morph (colors cross-fade A→B; one-sided data carries), Text On Path, Clone To Points (both single-shape and palette variants), Shape Exploder, Thicken (both stroke contours inherit the vertex they hug), Subdivide Shape (corners copy onto the boundary, interiors blend — gradients fill the mesh), and Shape Boolean (no vertex correspondence exists after clipping, so its arrays are present-and-defaulted rather than silently dropped). The rule is now enforced across the engine — no geometry operation may silently drop an attribute column — and the full survival matrix is published in the geometry attributes guide. This changes pixels in existing projects — chains that always should have shown gradients now do. Behavior change:
indexis an order, so deleting elements renumbers the survivors 0,1,2,… instead of leaving holes. - Attribute fields now show you what's on the stream. Every parameter that asks for an attribute name — fifteen of them, across Point Delete, Path Delete, Clone to Points, Draw Instances, Point Relax, Trim Path, Round Corners, Label Points, Shape Along Path, Attribute to Field and both Attributes nodes — was a text box barely nine characters wide with no list, no autocomplete and no way to know whether what you typed existed. They are now a proper picker: the
@properties with a line each explaining them, then the attributes actually flowing in from upstream with their type, count and value range, exactly as the Details tab shows them. You can still just type a name, including one nothing writes yet. An empty field states what the node does when it's empty — "All points" on Clone to Points, "Nothing deleted" on Point Delete — instead of leaving you to guess. A name that isn't on the incoming stream gets an amber warning listing what is; a name we couldn't check says nothing rather than crying wolf. And the two fields that create an attribute refuse an@name outright, because that namespace belongs to the computed properties. - Point properties you can finally name. Every field that asks for an attribute name now also accepts a property the point already has, written with a leading
@:@xand@yfor position,@indexfor its place in the list,@indexnormfor that as 0–1,@nfor the count,@idfor the stable identity that survives filtering, and@rand01for a random value that holds still as points come and go around it. Nothing has to be written upstream first. Point Delete filters by height with@y; Clone to Points clones a stable random fifth of a scatter with@rand01; Point Relax gives every point its own radius with@rand01; Label Points prints{@id}; and Attribute to Field turns any of them into a field, which hands position awareness to every field-driven node in the catalog at once.@is reserved — your own attributes can never be named with one, so the two can never be confused. The per-path fields (Draw Instances' time offsets, Trim Path and Shape Along Path delays, Path Delete, Round Corners, Clone To Points' Group By, Shape Attributes' Group By) resolve through the same reader, with their own trio —@index,@num,@indexnorm— sodelayAttribute: @indexnormstaggers a multi-path shape evenly with no tagging node, and Path Delete can drop "every path past the 3rd" directly. Point-only properties like@yare refused on path fields with a reason, not silently read as zero. @indexreplaces@elem(and@indexnormreplaces@elemnorm). One word for one idea:@indexis the element's position in the list, always available under Computed. Typing bareindexreads the same value — unless something upstream deliberately wrote anindexorder (Point Attributes' Index target driven by a field), which then wins. Generators (Grid, Point, scatter, text, resample, connect…) no longer stamp a storedindexcolumn, so "On this stream" shows only data something chose to write. Old projects keep working:@elem/@elemnormstill parse as aliases, and bareindexresolves everywhere it used to.- New node: Select — turn a rule into a group. One node answers "tag these": a range (
first 50), every Nth element, an attribute comparison (scaleAt Least 2,@xBetween −100 and 100), a stable random percent (keyed on point ids, so the subset survives edits upstream), or everything inside/outside a shape or mask field. Modes compose — chain a second Select set to Intersect for "right of center AND big" without any expression syntax — and the result is an ordinary attribute, so every Apply To field, Connect Points' From/To, Attribute to Field, and the delete nodes consume it directly. Point Ops category; works on points or shapes (per-point or per-path). While a Select is selected, the viewport shows its members bright and the rest dim — the group is visible before anything consumes it. - Named groups: tag two point sets, then connect one to the other. Tag each branch with its own group (Point Attributes, Custom target —
group1on one grid,group2on the other), merge, and Connect Points' new From and To fields draw only the bridges — neither set webs internally. The same name in both fields meshes that group alone. A group is just an attribute name, so points can belong to several, andAttributeToFieldturns any group into a 0/1 mask field. The From/To fields also take comparisons — a Rule dropdown appears once a name is set, so "connect points with scale over 4 to the grp2 set" needs no tagging node at all. Works in Nearest, Radius and Delaunay. - Rule-based delete. Point Delete and Path Delete gain a Rule dropdown: alongside the classic attribute/mask threshold,
Rangedeletes an element-order span andEvery Nththins periodically — "delete every 3rd point" no longer needs an attribute at all, and Invert flips any rule into "keep only these." - Apply To on six more geometry nodes. Draw Points, Shape Smooth, Subdivide Shape, Point Relax, Points Along Path and Point Advect gain the standard Apply To row at the top of the panel — name an attribute, intrinsic or group and the node acts only on that selection, with the same Rule/Value comparisons as Connect Points' From/To (
scale · Greater Than · 4). Each node applies it the way you'd hope: Draw Points draws only the selection, Shape Smooth pins the rest as anchors, Subdivide Shape passes unselected paths through, Point Relax keeps unselected points as immovable repellers (pinned-anchor layouts), Points Along Path picks which sub-path to distribute along, and Point Advect holds unselected points in place while the rest flow. Picking an intrinsic like@xauto-switches the rule to a comparison (@x · Greater Than · 0= right of center) — "member of a coordinate" was mental gymnastics, so the rule now speaks the attribute's own units. Applies to Connect Points' From/To too. - Merging no longer eats attributes that only one branch carries. Merge Points used to keep an attribute only when every input had it, which silently deleted per-branch tags. An attribute on any input now survives: missing entries fill sensibly (
scale/opacityat 1 so merged-in points stay visible, group tags at 0 = not a member).indexrenumbers across the merged stream, andsourceIndexstays distinct however deeply merges nest. - Vectors and colors are readable wherever a name is. Pointing one of these fields at a Vec2, Vec3 or color attribute used to do nothing at all, silently —
velocityfrom Track Points was unreadable everywhere. They now reduce the same way in every node: vectors by length, colors by luminance. - Connect Points: field-driven connection order. Sequential mode now follows the per-point
indexattribute (neworderByparam, default Index), so rewritingindexwith Point Attributes — from a Distance Field, noise, image luminance, any field — re-routes the polyline through the points in that order. Connect a scatter in SDF-distance order, pair with Trim Path for grows-outward reveals, or animate the field and watch the thread re-route live. Every point source stampsindexpositionally, so existing Sequential networks are unchanged;orderBy = Inputforces raw point order when you're usingindexfor variant selection instead. - New node: Point. One point at a position — that's it, and that's the point. No more coaxing a Grid into degenerate dimensions when you just want a single point to clone onto, advect, or build from. Count stacks several at the same spot with distinct identities, so a field or Random can fan them out — an instant burst from one location.
- Shake a node to disconnect it. Wiggle a node side-to-side while dragging and it pops free of all its wires without interrupting the drag — grab a Blur out of one chain and drop it into another in a single motion. With Heal on delete enabled (the default), the chain reconnects around the departed node. The pop is one undo step, and the node flashes briefly so it never reads as a glitch.
- Node shortcuts: bind your go-to nodes to keys. Preferences → Shortcuts has a new Node Shortcuts section — search for any node, press a key combination (say ⌥T for Transform 2D), and from then on that combination creates the node at your cursor whenever the node graph is focused, with the same auto-wiring as the menu. Shortcuts are per-machine, conflict-checked against existing bindings, and removable with one click.
- Drop a wire on empty canvas to create the next node. Releasing a dragged connection over empty graph space now opens the node menu right there, filtered to nodes that can actually accept that wire, with a Suggested group on top — pick one and it's created and connected in a single step (and a single undo). Works dragging backward from inputs too. The regular right-click/Tab menu also shows Suggested picks when it's about to auto-connect to the selected node. Suggestions blend sensible defaults per port type with the nodes you actually create most (tracked on this machine only). Escape or clicking away still cancels the wire — and wires released over another panel no longer stick to the cursor.
- Node search now forgives typos and knows nicknames. The node menu's search matches skipped-letter queries ("trns" finds Transform 2D), word initials ("cs" finds Color Split), and the names you might type coming from other tools — "t2d", "gaussian", "particles", "bloom", "wiggle", "pixelate", and friends. Exact and prefix matches still rank first, so existing muscle memory is untouched.
- File → Export… menu item (⌘⇧E). The render dialog no longer depends on the timeline toolbar's Render button, which narrow windows can squeeze out of view — the File menu (and its shortcut) opens it from anywhere, even with the timeline panel closed. The same trial/license rules apply as the button.
- Release notes on the docs site. docs.caddis.app now has a Release Notes page with the full history of what changed in every shipped version, updated automatically when a release goes out.
Changed
- Properties sections remember whether you folded them. Collapsing a section now sticks — per node type, across sessions — so you set up a panel the way you like it once. A folded section that contains something you've changed shows a count next to its name, and a section you've never touched opens itself rather than hiding an active control behind a default you didn't pick.
- Stray controls no longer sit above the ones you came for. A handful of parameters had no section assigned and, through a quirk of how the panel was built, that pushed them to the very top — so Trim Path opened on a delay field you can only use in one mode, and Text opened on eighteen OpenType checkboxes instead of the font. Unassigned controls now sit at the bottom where they belong.
- Much faster frames with heavy point and shape geometry. The engine used to send every geometry node's full point list, path list and attribute summary back to the app on every single frame, whether or not anything was looking at it — at a million points that was two thirds of the frame spent serializing data straight into the bin, and it multiplied by the number of geometry nodes in your chain. It now sends only what the interface is actually showing: the selected nodes, whatever the viewport gizmo is editing, and one hop upstream of those. Nothing about what you see changes; scrubbing and editing dense point work just stops stalling.
Fixed
- Point Advect no longer pulls points back to their seeds when the field animates down. Keyframing a field's strength (or evolution, scale — any animated field) invalidated the simulation every frame, so each frame re-ran the whole history with that frame's field frozen: as the field died, the replayed history integrated to nothing and every point snapped home. The simulation now replays history with the field evaluated at each past frame, so a weakening field slows points where they are — and scrubbing, exporting, or reopening a project reproduces sequential playback exactly, from any frame. Playback with an animated field is also much faster: it no longer re-simulates from frame zero on every frame.
- Playback no longer freezes on one frame downstream of a baked File Cache. After baking, a File Cache serves frames from disk and skips evaluating the nodes above it — but the nodes below it were handed a cache key that never changed from frame to frame, so Draw Points and friends kept serving whichever frame rendered first, with occasional jumps when the quality level flipped. The served file's frame and identity now feed the downstream keys, so cached playback advances every frame like it should.
- Dark seam where antialiased shapes overlap. Overlapping shapes showed a subtle dark outline along the top shape's edge (most visible white-on- white — up to 12% darker at half-coverage pixels). The GPU shape renderers produce premultiplied-alpha edges from MSAA, but the rest of the engine composites straight alpha, so edge pixels were effectively darkened twice. Every draw producer (Draw Shape, Draw Text, Draw Glass, Draw Instances, Draw Points) now converts to straight alpha before handing off. Antialiased edges also composite correctly over any backdrop now, not just this white-on-white case.
- Hardened Draw Instances / Slitscan against garbage frames on memory-constrained GPUs. On some Windows machines, instance palettes could show solid blocks of uninitialized GPU memory (often white) after a non-fatal device error — Macs hid the same failure because their drivers hand back zeroed memory. Palette assembly now verifies source sizes and falls back to a full-coverage blit (no layer can be left uninitialized), array sampling avoids a shader pattern some drivers miscompile, and the status-console GPU warning now includes the device's own error message — naming exactly which allocation failed — instead of a generic notice.
- Connection strands and trails now actually blend point colors. Point color has always been copied onto each strand's endpoints, and the docs promised gradients — but the renderer only applied per-vertex color to fills, and strands are open paths drawn as strokes, so they came out in the flat stroke color (white). Strokes now interpolate endpoint colors along their length on the GPU: connect a red group to a blue group and every bridge runs red→blue. (Dashed strokes and text outlines keep their flat color — the dash splitter and rich-text color rules are unchanged.)
- Trim Path's staggered delays now complete — and don't snap. A delayed path could only ever reach partway (
endcan't exceed 1, and the delay was subtracted straight off the window), so at 0.999 a staggered shape sat half-drawn, then leapt to finished at exactly 1 through an unrelated shortcut. Delays are now a proper stagger: values are normalized order (0–1, exactly what@indexnormsupplies), a new Stagger control sets how much of the animation they occupy, and every path completes smoothly at exactlyend = 1. Existing delay setups render differently — they were relying on the snap to finish at all. - Thickened gradient strokes rendered flat white. Thicken tags its contour pairs with a glyph group to knock out donut holes, and the per-vertex color renderer excluded glyph-grouped paths from colored strokes (a rich-text rule). Gradient outlines now stroke as gradients.
- Subdivide Shape ignored its Grid Size. The bounds pre-pass read a differently-named parameter (always the 20px default) and its result was reused for rendering, so changing Grid Size did nothing. It now takes effect.
- Point Trail's two outputs no longer overwrite each other. Point Trail emits both a shape and a point stream, and the app filed them under the same key — so panels and gizmos asking about one could be handed the other, depending on which arrived last. Each output is now tracked separately.
- The Details panel and attribute pickers no longer go stale when you change selection. Selecting a node after a frame had cached could show an empty Details tab and an attribute list missing names that were really there — the cached frame was answering with the captures requested by the previous selection. Cached pixels are still reused; the inspection data now refreshes whenever the selection asks for more than the cache holds.
- Point Delete no longer opens with an error on a control you haven't touched. Its Attribute field shipped pre-filled with
selection— a name nothing in the app ever writes — so a freshly dropped node flagged itself as broken while doing exactly what it should (nothing). The field now starts empty, and existing projects carrying the old value are cleared on load. - An attribute field connected to a Mask input no longer describes the wrong behaviour. A connected Mask overrides the typed name entirely, but the field went on stating what would happen if it were empty. It now shows as driven, like every other overridden control.
- A typo in Draw Instances' offset attribute no longer produces a plausible cascade. A missing or misspelled name used to fall back to the point index — sequential offsets that looked intentional. It now reads as no offset, and the field flags the unresolved name.
- Stroke Color and Name are built-in attributes again. The geometry Details panel was badging
strokeColorandnameas custom attributes and sorting them below your own, because the editor kept its own hand-written copy of the built-in list and that copy had fallen behind the engine. The list is now generated from the engine, so it can't drift again. - Timecode readout clipped at larger text sizes. With Text Size at 125% or 140%, the timeline's current-time box (and the zoom percentage next to it) overflowed its fixed width — both now grow with the text setting.
Security
- Caddis now refuses to run if its own code has been modified. The macOS and Windows builds ship with Electron's asar integrity fuses enabled, so the app verifies its bundled code against a hash sealed into the signed application before it starts, and will not load code from anywhere else. The debug entry points that let a packaged build be re-run as a plain scripting host are switched off in shipped builds too. This does not change anything you can see or do in the app.
1.0.0 — August 1, 2026
Added
- A new opening moment. The sign-in and project screens now open onto a living version of the caddis.app wave gradient — the site's colors flowing and evolving under film grain, dimmed to app brightness — instead of a flat dark panel. It renders on the GPU for next to nothing, pauses when the window is hidden, and stays still if your system asks for reduced motion. Buttons and fields on these screens pick up light outlined strokes, and the purchase card drops its beta-era ask for a straighter pitch: one purchase covers all of Caddis V1.
- Free 7-day trial — Caddis is out of beta. Signing in with any email now creates your account and starts a free 7-day trial of the full app: no waitlist, no manual activation, nothing held back. The trial clock starts the first time you open Caddis (not when you sign up), and the status bar shows how many days remain. Beta testers roll into the same fresh 7-day trial automatically — same sign-in, same projects, same devices.
- "Get Caddis" purchase modal. Buying a license now has a proper in-app surface: what you get (a perpetual license for all of Caddis V1, unlimited exports, 2 devices — no subscription), the price, and a checkout button. Checkout opens in your browser with your email prefilled, and the license activates automatically when you come back to the app — no key to paste, no restart.
Added (nodes)
- Connect Points + Label Points — point clouds that explain themselves. Two new Point Ops nodes. Connect Points turns any point stream into a line network: link each point to its nearest neighbors, join everything within a radius, build a clean Delaunay mesh, outline the convex hull, or thread one polyline through the points in order — the classic plexus / web / constellation looks in one node. A
fadecontrol dims longer strands, endpoint colors blend into per-strand gradients, and the output is ordinary shape geometry, so TrimPath draw-ons, ResampleShape, and per-path styling all just work. Label Points puts a live text label next to every point —{index},{x}/{y}coordinates, or any per-point attribute via{attr:name}— rendered as real vector glyphs with font, size, offset, and anchor controls, plus the same mask selection convention as Point Delete for labeling only the points you care about. - DrawAscii: character size decoupled from grid density. New
Char Scaleparameter scales each glyph about its cell center while the sampling grid stays put — so a 4K comp can keep a dense, detailed grid without the letters shrinking to specks. Below 1 adds padding around each character; above 1 (up to 8) the letters grow past their cells and overlap their neighbors for chunky, poster-style ASCII, with the glyph atlas automatically re-rasterized at matching resolution so big letters stay sharp. Default 1.0 renders exactly as before.
Fixed
- PointDeform Normal/Tangent modes now push evenly all the way around a circle. Driving them with a scalar field (a wave, noise) secretly scaled the displacement by each point's rotation angle — points near 135° and 315° barely moved and half the ring moved backwards. Radial rigs (wheels, rings, orbits) now respond to the wave uniformly.
- Tab now hops cleanly between parameter fields. Typing a value and pressing Tab commits it and moves straight to the next field with its value selected — type immediately, no clicking. Shift+Tab goes backwards, and the cycle wraps within the Properties panel. Previously Tab wandered onto dropdowns and toggles (lighting them up with a web-style focus ring), and a second Tab could trigger the node graph's Tab shortcut and pop the node menu out of nowhere. The bright keyboard-focus ring on dropdowns is gone too.
- Oscillate no longer clips tall waves into plateaus. Turning amplitude above 1 used to slice the top and bottom off the wave — a sine driving ShapeDeform produced flat-topped bumps instead of taller arcs. The wave now scales freely for geometric uses (deform, particle motion); only the normalized 0–1 reading (color ramps, opacity) still clips at the rails, as a normalized signal should.
Changed
- A finished trial no longer locks you out. When a trial ends you can still sign in, open projects, and keep working — only rendering is gated. The Render button dims and opens the purchase modal instead of the export dialog, so nothing dead-ends into an error. (Beta builds locked the whole app at expiry.)
0.2.5 — July 26, 2026
Added
- Link toggle for the graph editor's In/Out ease boxes. A chain-link button between the two influence fields (same icon as linked parameters like Scale) mirrors an edit on either side to both — each keyframe takes the sides it actually has.
Fixed
- Imported SVG logos no longer render random letters as outlines or fill in letter counters. Whether a contour without an explicit closepath imported as closed depended on invisible floating-point differences in where it sat on the canvas — the same letter shape could fill at one position and import open (outline-only, with its counter hole filling solid) at another. Contours now close within a sensible tolerance, and fill-only artwork follows SVG's rule that fills always implicitly close.
- Merge input order no longer changes on its own. Inserting a node onto one of a Merge's input wires (or healing around a deleted node) silently moved that input to a different position in the stack, and edges without a stored position could reshuffle after unrelated graph edits — with the Properties list and the render disagreeing about the result. Every connection now gets a stable position, editor and engine order edges identically, and new connections land on top of the stack.
- The Merge/Shape Merge input list now reads like a layer stack. The top row in Properties is what renders on top, and the wire fan on the node's input pill matches (top slot = top of composite). Sequence-style inputs (Shape Morph steps, Merge Text, instance sources) still read first-to-last. Reorder drags also track the pointer accurately over long distances.
- Ease In/Out boxes no longer lock up for group selections. Selecting a run of keyframes that included the first or last key in a curve greyed out the In (or Out) influence box for the whole group. It now stays editable whenever any selected key has that segment; keys without it (the very first/last) are simply skipped.
- The playhead grab handle is back in the Keys and Graph timeline views. The triangle at the top of the playhead line was only visible in the Layers view: the Keys (dope sheet) view drew it underneath the opaque frame ruler, and the Graph editor never drew one at all. Both views now show the same draggable triangle as the Layers view.
- Published values no longer freeze the whole viewer with a false "circular dependency" error. A Value Source node that wasn't wired to anything — parked in the graph mid-edit — could still register a cross-layer dependency; combined with a publish flowing the other way, the engine refused to render every frame of the comp. Layer ordering is now computed only from consumers that actually evaluate, so stranded nodes and param publishes can never manufacture a cycle. Genuinely circular wiring is still rejected — and instead of a silently black canvas, the viewer now shows a banner naming the layers in the loop (e.g. "Box" → "Controls" → "Box") in plain language, and the node graph highlights the exact nodes whose references close it with a red border and a CYCLE badge.
- Editing or animating a published value updates its consumers again. Nodes downstream of a Value Source cached their results without accounting for the published value itself, so dragging a published Constant (or keyframing it) left consumers replaying a stale frame forever. Published values now flow into the cache keys, exactly like Layer Source references.
- Publishing, unpublishing, renaming, or re-pointing a published value now refreshes the frame. These edits live on the composition rather than any layer, and the frame cache never noticed them — the viewer kept showing the pre-change image until an unrelated edit forced a re-render.
- Renaming a published value updates every consumer automatically. Value Source and layer-parenting consumers used to keep the old name and silently go dead; the rename now travels to all of them in a single undo step.
- A missing publish no longer snaps consumers to zero. If a Value Source's publish name can't be resolved (renamed or deleted publish), the driven parameters now keep their stored values instead of collapsing to 0 — previously position jumped to comp centre and scale collapsed, making content vanish.
- Fixed the Published panel corrupting parameter publishes on rename. Editing a param publish via the pencil button created a broken duplicate entry that resolved to nothing. The edit now renames correctly, and broken entries already saved in project files are cleaned up automatically on load.
- Published textures no longer crop, shift, or crash when layer canvases differ. Reading a published texture from a layer with a different internal canvas size performed a raw copy that cropped the image — or triggered a GPU validation error. Mismatched sizes are now resampled.
- Render and project-sync failures now show a dismissible banner in the viewer (and log to the status console) instead of failing silently to the developer console.
0.2.4 — July 21, 2026
Fixed
- Image-sequence import no longer falls apart when a frame can't be read. Importing a numbered run (e.g. Blender's
0001.exr,0002.exr, …) used to silently fall back to individual still images if the first frame's header couldn't be read at that moment — which is exactly what happens with cloud-placeholder files (OneDrive/Dropbox online-only), antivirus holds, or transient locks, especially on Windows. The sequence is now detected from the directory scan alone; dimensions are filled in on first render. And if File → Import Image Sequence can't import the picked file, it now says why in an error dialog instead of silently doing nothing. - DWAA/DWAB-compressed EXRs now decode. EXRs saved with the DWAA or DWAB codec (Blender's smallest-file option) previously rendered as fully transparent frames — the built-in decoder doesn't support those compressions. They now decode through the bundled ffmpeg automatically, at full float precision. Applies to single EXRs and sequences alike.
- "New Project" now fully clears the previous project. Asset folders — and other editor-side organization like color tags, guides, and node groups — no longer carry over from the previously open project when creating a new one (or when opening a project file that doesn't have them).
- Faster renders when content sits far outside the frame. Layers whose content extends way off-canvas (oversized guide shapes, elements pushed aside) no longer force giant hidden render canvases: the engine now proves which regions can never reach the final frame and skips them — up to ~9x faster per frame on affected scenes, with pixel-identical output. Scenes where the off-canvas content later re-enters the frame render exactly as before. (
CADMIUM_DISABLE_OVERSCAN_CAP=1restores the previous behavior.) - Much faster renders for layers with far-off-center content. Layers whose elements travel a long way from the frame (arms sweeping in from off-screen, parked passes that slide through) previously rendered on a single giant canvas stretched to reach them. The engine now renders each node into a small, precisely-placed window instead — 4-5x faster per frame on affected real-world projects, with no change to how anything is authored. Layers using nodes outside the supported set automatically keep the previous behavior. (
CADMIUM_DISABLE_OVERSCAN_OFFSET=1restores the old canvases wholesale.) - Blur edges no longer depend on hidden canvas size. Clamp-edge blurs used to smear whatever happened to sit at the edge of the layer's internal render canvas — an invisible implementation detail that could change the picture when the canvas resized. Blur edges are now derived from the content itself, so results are stable and slightly more correct near the frame edges on affected layers. (
CADMIUM_DISABLE_CLAMP_BLUR_FIX=1restores the old behavior.)
0.2.3 — July 20, 2026
Added
- More shortcuts, all rebindable. Timeline:
Enterrenames the active layer,Deleteremoves selected layers,Alt+↑/Alt+↓move the active layer in the stack,Cmd+Shift+Dsplits the selected layer(s) at the playhead (AE convention — previously button-only), andCmd+Shift+Rreverses the stacking order of the selection. Assets:Enterrenames andDeleteremoves the selection panel-wide. These now live in the same customizable command system as everything else, so each can be rebound in Preferences → Shortcuts. - Customizable keyboard shortcuts. Preferences (
Cmd+,) gains a Shortcuts tab listing every command by area with its current binding: click one, press the new combination, done. Search, per-shortcut reset, and Reset All included. If the keys are already in use in the same context you choose — Take Over (moves the shortcut, the other command loses it), Keep Both (context rules decide which runs), or Cancel. Customizations are per machine; menu actions (Save, Open…) and structural keys (Escape, Undo) stay fixed for now. - Delete removes the selected layer(s) from the viewer. With a layer selected in the viewport (Select mode),
Delete/Backspacenow deletes it — same as the timeline's delete button, one undo step. Edit mode keeps its meaning (deleting shape points), as does the Pen tool. - Cmd/Ctrl+D duplicates compositions from the Assets panel. Select one or more comps in Assets and duplicate them in place — the copies are selected afterward, and a multi-comp duplicate is a single undo step.
Fixed
- Alt+↑/↓ moves the active layer in the stack again. The layer-reorder shortcut was unreachable — an arrow-key guard in the layer list swallowed Alt+arrows before the reorder code could run.
- Dope sheet: Cmd+V no longer double-pastes. With a keyframe clicked and both clipboards populated, pasting could apply the selected-keyframe paste AND the param-row paste in one keystroke. Keyframe selection now takes precedence; param-row copy/paste applies only when no individual keyframes are selected.
- The
Ieyedropper shortcut works. Documented as "activate the eyedropper while a color picker is open", it was wired to plumbing that no longer existed and did nothing. With a color picker open,Inow arms that picker's eyedropper — same as clicking its eyedropper button: click the viewport to sample, Esc cancels. - E enters edit mode from the transform the viewport selected. Clicking a shape in the viewport selects its Transform — pressing E then did nothing until you manually clicked the shape node. E now walks upstream from the selected node to the shape or text feeding it (per branch, so the right shape is targeted when several are merged) and opens the editor on it.
- V reliably returns to plain selection. Pressing V now also cancels the active tool — previously it set the viewport mode but left the Pan tool (H) engaged, so the viewport stayed in pan mode.
- Colorize keeps transparency. Putting Colorize after anything with transparent regions — a matte, a shape on an empty canvas — flooded the whole frame with the ramp's darkest color, destroying the transparency. The output now carries the source's alpha through (output alpha = source alpha × ramp alpha), and brightness is read from the true un-premultiplied colors, so soft matte edges no longer read darker than they should. The same applies to the
colorFieldoutput when Colorize re-maps another color field. To colorize the full canvas from a transparent source on purpose, Merge it over a Solid first. - Math node: the B input now drives Snap's step. With the operation set to Snap, wiring a value into B did nothing — the node only read the Step param, which has no port. A connected B now sets the snap step from upstream; the Step param still applies when B is unwired.
0.2.2 — July 15, 2026
Added
- Missing-file badges and relinking in the Assets panel. Assets whose file can't be found on disk now show a red Missing badge (re-checked whenever the app window regains focus). Right-click any asset for a new context menu: Relink / Replace File… points the asset at a new file and every node that referenced the old one follows automatically — one undo step, metadata re-read from the new file. Rename and Remove round out the menu.
- Composition duration in seconds. The Duration field in Composition Settings now has a Frames / Seconds switch — type
30seconds instead of computing frame counts in your head. The helper text shows the equivalent in the other unit, the choice is remembered, and changing the frame rate preserves whichever unit you're working in (in seconds mode, a 30-second comp stays 30 seconds when you switch fps).
Fixed
- Windows: imported assets show their filename, not the full
C:\…path. Asset display names were derived by splitting on forward slashes only, so Windows paths never split. All name and sequence-grouping logic now handles both separator styles — image-sequence detection on Windows gets this fix too. Note: assets imported before this fix keep the name they were saved with — rename the row (double-click or Return) or re-import to clean them up; newly imported assets are unaffected. - Assets panel: the "In Use" tag now covers every file type. It only checked Image and Video Source nodes, so audio, SVG, image-sequence, and LUT assets never showed as in use. The check now matches any node that references the file, so new source types are covered automatically.
- Shared project folders: source nodes find their files on the new machine. Opening a shared project resolved assets in the Assets panel but left every Image/Video/Vector Source node pointing at the previous machine's absolute paths — forcing a re-drag of each node from the panel. Node paths now follow their asset's resolved location, files inside the project folder (like
generated/AI outputs) re-anchor automatically, and relative paths saved on Windows resolve on Mac and vice versa. - SVG import: wordmarks and outlined text from Affinity Designer render correctly. Contours that return exactly to their start point without an explicit closepath (
Z) — how Affinity exports letter outlines — were imported as open paths, so letters lost their fill and counters rendered filled instead of cut out. They now import as closed, filling and subtracting the way every other SVG renderer treats them.
0.2.1 — July 10, 2026
Added
- Text Animate: overshoot and bounce, drawn on the progress curve. The In/Out progress params are now open-range — keyframe past 1 (or below 0) in the graph editor and the letters travel past their resting position and settle back; opacity and blur stay clamped so nothing flashes. A new Stagger Mode on the Timing group chooses how characters share that curve: Time (the default for new Text Animate nodes) delays the full curve per character by Stagger Frames, so a multi-bounce landing drawn once on the curve plays for every character at its own landing; Progress slices the 0→1 range per character as before, with out-of-range values moving the whole line together. Existing projects keep their current look, and the progress rows show a small "(overshoot)" tag whenever the value at the playhead is outside 0–1.
0.2.0 — July 9, 2026
Added
- Variable font support. Selecting a variable font on a Text node exposes its design axes — Weight, Width, Slant, Optical Size, and any custom axes — as regular keyframeable sliders in the Typography section. Axes are continuous: keyframe Weight from 100 to 900 and the glyphs, spacing, and kerning all interpolate smoothly. Values clamp to the font's declared ranges, switching between variable fonts carries matching axes over, and static fonts keep the familiar named-weight dropdown.
- Fonts that travel with the project. New projects scaffold a
fonts/subfolder, and any font files in it load automatically when the project opens — alongside system fonts. Hand the project folder to a teammate and it renders with the same typefaces, nothing to install. (Existing projects: just create afonts/folder next to the.caddisfile.) - Format Number node (Text category) — turns any scalar into display text: fixed decimals, zero-padding (
0042), thousands separators, prefix/suffix. WireTime → Format Number → Textfor frame counters, timers, percentage readouts, and price tags without leaving the graph. - Rich text: style part of a paragraph. Select characters while editing text in the viewport and a style bar appears — change the weight, size, or color of just the selection, or Reset it back to the base style. Mixed sizes grow their line to fit, styles stick to the text as you keep typing and editing, per-character colors survive Text Animate reveals and blurs, and everything flows through Text to Shape for further graph work. Range colors are fill colors: a stroked (outlined) text keeps its outline color on recolored words, in both Draw Text and the shape pipeline. Weight styling works on static families and variable-font Weight axes alike.
- OpenType typography controls. A new OpenType section on the Text node exposes the font's real typographic machinery as toggles: kerning, standard and discretionary ligatures, contextual and stylistic alternates, small caps, fractions, oldstyle figures, tabular figures (fixed-width digits — the fix for jittery counters and timers), and stylistic sets 1–8. The panel lists only the features the current font declares, so it stays honest and compact.
- Case transform. A Case control (Typography section) renders text as UPPERCASE, lowercase, or Title Case without touching what you typed — applied before shaping, so ligatures and kerning see the final letters, and editing, per-range styles, and Text Animate keep working on the original text.
- Baseline shift and small caps for a selection. The viewport style bar gains two controls: shift just the selected characters up or down (ink-only — line spacing is unaffected), or flip them to the font's small-caps forms (the toggle only appears for fonts that provide them). Both stick to the text through edits like the other per-range styles.
- The style bar understands base vs. override. With the whole text selected, weight and size changes write the Text node's own params — the Properties controls (including a variable font's Weight axis) stay live instead of being masked by a full-length override. And on variable fonts the bar's weight control becomes a numeric field driving the weight axis, replacing the nine fixed names. Bar numbers scrub like Properties fields: drag to adjust (Shift = coarse, Alt = fine, Escape reverts the drag), click to type. Color follows the same rule: a whole-text pick writes Draw Text's Fill Color — the painter stays the painter — while a sub-range pick colors just those characters.
- Character styles are visible — and keyframeable. The Text node's Details tab lists every styled range (the affected text, swatch, and overridden properties) with a one-click Clear, and Draw Text calls out when upstream character colors override parts of its fill — no more silently dead parameters. A keyframe diamond on the Character Styles section hold-keyframes the whole style set, the same stepped model as keyframed text content: style-bar edits then land on the keyframe at the playhead. (Smooth per-glyph animation remains Text Animate's job.) Undo granularity inside a text edit session matches the rest of the app: each style action is one undo step, a scrub or color-picker session is one step, and typing coalesces into one step per burst — Cmd+Z peels changes off action by action instead of reverting the whole session.
- Per-axis point scale — a new Scale X/Y target on Point Attributes. Alongside the uniform Scale target, Scale X/Y writes an independent X/Y scale per point: set a linked (unlinkable) X/Y value, or drive per-axis strength from a vector field. Clone to Points stretches instances along their local axes, and Draw Points renders per-axis-scaled points as ellipses. Existing uniform scale setups are unchanged.
- Drag multiple assets at once. Dragging a row that's part of a multi-selection now carries the whole selection: drop on a folder to file everything (compositions included), or drop on the viewer/timeline to create a layer per asset — as a single undo step.
- Importing one frame of an image sequence now asks. When a single imported image matches a numbered run on disk, a dialog offers Import Sequence or Import Single Image instead of silently pulling in the whole sequence. Multi-file drops still auto-collapse, and File → Import Image Sequence remains the explicit path.
- Double-click anywhere on a layer's row to rename it — previously only the name text itself responded, so near-miss clicks did nothing. Return still works.
- Draw Instances node (Render category) — Clone to Points for pixels. Stamp any rendered texture at every point in a Points set: a whole effect chain (an animated shape with its glow and shadow intact), an image, or another layer's output via Layer Source. Instances read the same per-point attributes cloning already uses — rotation, scale, opacity, color tint — and render as a single instanced GPU draw, so point order is stacking order. Connect several sources and they form a palette; Selection Mode picks per point: Sequential, Random (seeded), or Point Index (the index written on each point). Built-in cell fitting turns mixed-aspect images into uniform tiles — Fit or Fill into a cell size, with rounded corners — so an animated image grid is just images + Draw Instances + a Grid of moving points.
- Staggered copies — per-instance time offsets on Draw Instances. Each instance can play the source at its own delay: Sequential cascades by a fixed step, Random shuffles the delays, Attribute reads a per-point value. One animated source becomes a wave of copies, each a few frames behind the last. Offsets sample into a bounded set of time slots (Max Time Samples), and the result is a pure function of the timeline — scrub anywhere, play, or export and it always renders the same. For heavy chains, drop a File Cache upstream and offset frames read from disk; the node shows the same "may be slow" hint as the other time nodes when that would help.
- Clone Index node (Utility category) — a for-loop over copies. Place it in the chain feeding a Draw Instances and the chain re-evaluates once per instance, with Index / Count / Normalized / Random outputs describing which copy is being built. Wire Index through a Math node into a shape's radius and five copies render at five sizes; use Normalized for smooth ramps across the set, or the seeded Random for stable per-copy variation. Capped by a Max Clones control, and it composes with time offsets — each copy gets its own index and its own delay.
- Index is now a first-class target on Point Attributes and Shape Attributes. Randomizing or ramping which variant each point shows used to mean writing a custom attribute by its internal name — now it's just target: Index, driven by a field, a value, or the index ramp. Clone to Points and Draw Instances read it directly in their Point Index selection mode (renamed from "Attribute" for clarity), and Draw Instances' time offsets read the same attribute by default — one written index can drive variant choice and stagger together.
- Glow grows up: real falloff, real reach, and an isolated output. Radius now means what it says — the distance the glow visibly travels — and a new Falloff control picks the shape by look, not by number: Soft (the classic single-blur glow), Natural (bright core with a natural halo), or Long Tail (a tight core with long atmospheric tails). Under the hood the layered falloffs stack blur octaves at doubling radii with the core always at full strength, so pushing Radius makes a small shape bloom out across the whole frame instead of washing away. A linked Stretch X/Y pair bends the glow directional for anamorphic streaks, and a new Output option renders the glow by itself, no source underneath: grade it, distort it, stamp it with Draw Instances, or composite it back on your own terms. Existing projects keep their exact look (they load as Soft).
- Preferences, with an Appearance tab. A new app settings dialog (Cmd+, on macOS under Caddis → Settings…, Ctrl+, on Windows under Edit → Preferences…) — the home for app-wide options going forward. First up, the most-requested readability controls: Text Size (up to 140%) scales the type throughout the interface while panels, controls, and the viewport keep their size; and Text Contrast: High brightens labels and secondary text throughout the app. Both apply live and persist per machine. The default look is unchanged.
- Caches now scale to your machine. Every big frame cache used fixed one-size-fits-all budgets — a 192 GB studio machine got the same few gigabytes as a 16 GB laptop. Now the engine's frame cache uses a quarter of system memory (2–24 GB), RAM preview and the playback cache each scale to a quarter of memory (4–16 GB), and the GPU texture cache scales to the graphics hardware: an eighth of memory on Apple Silicon (where GPU memory IS system memory), and half of dedicated video memory on Windows graphics cards (a 24 GB card gets a 12 GB cache; a 4 GB card keeps a safe 2 GB). Big machines hold far more of the timeline and node graph hot; smaller machines get safer budgets than before. The env overrides (
CADMIUM_FRAME_CACHE_GB,CADMIUM_GPU_CACHE_MB) still win. - Cut connections with a swipe. Hold Alt and drag across any wires in the node graph — a dashed cut line follows the cursor and every connection it crosses is deleted on release, as a single undo step. No more clicking each wire individually.
- Deleting a node no longer breaks the chain. Delete a Blur sitting between an Image Source and the Output and the two reconnect automatically — including whole runs of deleted nodes, fan-outs to several consumers, and only when the port types actually match. On by default; the heal toggle lives in the node-graph toolbar next to auto-wire, and delete + heal is one undo step.
- The cache bar now shows resolution, and manual quality means it. Cached frames in the timeline ruler are colored by the resolution they were rendered at — green for Full, light blue for 1/2, gold for 1/4, matching the gizmo color family; RAM preview bars follow the same coding — and the viewer's quality dropdown wears the same color as a live tier readout. Auto-quality behavior explains itself at a glance. And picking a manual quality now evicts cached frames from other tiers, so switching to Full shows Full immediately instead of replaying a stale lower-resolution cache.
Fixed
- Running out of GPU memory no longer crashes Caddis. On graphics cards with less memory (4 GB class), a heavy scene could exhaust the GPU and kill the engine outright — the app became unusable. Three fixes: GPU errors now recover instead of crashing (caches are cleared and a warning appears in the status console — lower the viewer quality if it repeats); the engine's texture cache now has a real memory budget (2 GB by default, tunable via
CADMIUM_GPU_CACHE_MB) instead of growing without bound on deep graphs; and the Node Preview panel got its own cache — it was silently forcing the entire graph to re-render and re-allocate on every frame whenever it was open alongside the main viewer. - Tab now walks through all Properties fields. Tabbing previously dead-ended when it reached a curve widget (Remap curves, Levels curves, index curves), which sat in the tab order but ignored the Tab key.
- Playback no longer flickers between resolutions. Two compounding bugs. Auto quality kept re-trying a resolution tier it had just measured as too slow — heavy scenes (a very large Glow, say) oscillated 1/2 ↔ 1/4 forever. Upshifts are now predictive: the next tier is only attempted when its estimated cost actually fits the frame budget, and a failed attempt backs off exponentially before retrying. And the playback frame cache kept frames at whatever quality they were first rendered — mixed-resolution frames replayed as flicker on every loop pass even with a steady quality indicator. Cached frames now remember their tier and quietly re-render when it doesn't match. Auto quality also resumes from the tier it last settled on instead of re-paying the discovery cost on every play.
0.1.29 — July 3, 2026
Added
- Compositions now live in the project panel's tree. Instead of a separate list pinned to the bottom of the panel, each composition appears as a row at the top of the same tree as your assets — file them into folders, color-tag them, and find them by search. Click a composition to switch to it, double-click for settings. The New Composition button now sits in the tree's toolbar and doubles as a drop target: drop an asset onto it to create a composition sized to that asset.
- Shape Deform and Point Deform can weight X and Y independently. The Amplitude control is now a linked X/Y pair — unlink it to push a deformation harder along one axis than the other in XY mode. The single-axis and normal/tangent modes still show one value. Existing projects keep their current amplitude.
- Node Preview — a second viewport. A new panel (View → Panels → Node Preview) shows the currently selected node's output, so you can inspect a single node against the full composition side by side. It updates as you scrub and edit. Image-producing nodes preview directly; value/geometry nodes that only become pixels downstream show as transparent.
- Workspaces — save and switch between panel layouts. A workspace switcher in the top bar lets you save the current arrangement as a named workspace and jump between them. Update writes changes back into the active workspace (a dot flags unsaved layout changes), and you can rename or delete your own workspaces. A built-in Default is always available and can be reset to the shipped layout.
- Panels can now be closed and reopened. Hover a panel's tab for a × to close it — its space goes to the neighboring panel. Reopen any panel from View → Panels, which also brings tabbed-away panels forward; a reopened panel returns to the slot it was closed from. View → Panels → Reset Layout restores the default arrangement, and layouts with closed panels survive restarts.
- The project name now shows at the top of the window (Mac), centered in the title strip, with a dot marking unsaved changes — previously the title was set but never visible anywhere in the app.
- Copy / Paste in the keyframe right-click menu. Right-clicking selected keyframes (dope sheet or graph editor) now offers Copy and Paste at Playhead alongside the interpolation options — the pasted keys land with the earliest on the playhead, keeping their relative offsets.
Fixed
- The editor no longer fails to load from a panel layout it doesn't recognize. A saved layout that referenced a panel not present in the current build (e.g. after a downgrade) would crash the whole editor on launch. Unknown panels are now dropped from the layout on load, and the layout falls back to the default if nothing usable remains.
- The app shuts down cleanly on system termination signals, rather than leaving its background rendering process orphaned — which could interfere with the next launch.
- Unsaved-changes tracking works again. Edits were never being registered as unsaved: the title bar didn't show the unsaved dot, auto-save never ran, and quitting with unsaved work didn't warn. All three are restored.
- Nodes added from the node-graph toolbar now keep their placement. The position of a toolbar-created node was written under a malformed key, so the node could snap to a default location instead of where it was dropped.
- Text box gizmo follows a driven Box Size. When a Text node's box size was wired from another node, the edit-mode gizmo ignored the driven value and drew from the raw parameter instead.
- Editable Shape "Auto" close state resolves correctly. The tri-state closed control's Auto setting always resolved to open; it now defers to the drawn sub-path's own closed state as intended.
- Panel resizing works again around the Assistant panel. Dragging a divider on any split containing the Assistant panel silently failed (the resize math crashed mid-drag because the panel had no registered minimum size). Resizing is also hardened so a layout carrying an unrecognized panel can never break dragging again.
- Keyboard copy/paste of keyframes now works on Windows. The Edit menu's shortcut registration was swallowing
Ctrl+C/Ctrl+Vbefore the timeline ever saw them, so copying keyframes with the keyboard silently did nothing on Windows. The Edit → Copy / Paste menu items now also operate on selected keyframes (they previously only handled text). - Keyframes can now be placed at — and beyond — the end of a clip. Dragging keyframes in the dope sheet stopped one frame short of the composition's end, so an animation could never be made to finish exactly on the clip boundary. Keyframes can now be dragged onto or past the end; values hold after the last keyframe, and any keys beyond the boundary are preserved if the clip is later extended.
- Noise octaves now add detail instead of restructuring the pattern. Each octave used to be averaged into the total, which dimmed the base layer every time an octave was added — going from 1 to 2 octaves visibly changed the large shapes instead of refining them, and raising Roughness washed out the base as well. Octaves now stack on top of an unchanged base layer, and Roughness only controls the strength of the added detail. Octaves is also properly fractional now — values like 2.5 blend the last octave in halfway (previously only whole numbers had any effect), so animating octaves ramps detail in smoothly. Existing projects using more than 1 octave will render with slightly stronger base shapes; single-octave noise is unchanged.
- Invert no longer looks too bright. The Invert node now inverts in display space by default, so the result is the expected visual complement — a mid-gray stays mid-gray and a light tone becomes the equally-dark tone, instead of brightening. A new Space control lets you switch back to the old scene-linear inversion for physically-based compositing. Existing projects update to the new default automatically.
0.1.28 — July 1, 2026
Fixed
- Audio now works on Windows. The app couldn't locate its bundled
ffprobehelper when inspecting audio, so importing an audio file — or exporting any composition that contains audio — failed with a "program not found" error. Windows now finds the bundled tool correctly. (Mac was unaffected, as was video-only work on Windows.)
0.1.27 — July 1, 2026
Fixed
- The app no longer fails to start on PCs with certain GPU drivers. The engine used to probe every graphics backend at startup (including OpenGL), and on some machines a broken or outdated graphics driver would crash it during that probe — leaving the app unable to open at all. It now uses only the native backend (Direct3D 12 on Windows, Metal on Mac), which resolves the crash.
0.1.26 — June 29, 2026
Added
- Shader node — write, paste, or AI-generate custom GPU shaders — a new node that runs a fragment shader you author in WGSL, acting as a generator, filter, or compositor depending on how many image inputs you give it. Edit the code in a resizable Properties editor with line numbers and live compile-error markers; declare image inputs and tweakable params with a one-line pragma — or the + input / + param buttons — and they become node ports and keyframeable sliders/colors. A dedicated Assistant panel writes the shader from a plain-language description ("a VHS glitch with chromatic aberration"), exposes the knobs you ask for, and auto-fixes compile errors — using either Caddis AI (token-metered, choose from several models) or your own Claude/OpenAI key (called directly from your machine — the key never leaves it).
- Paste Shadertoy shaders (GLSL) — switch the Shader node's format to GLSL and paste a Shadertoy-style
mainImageshader. Procedural effects, noise, fractals, and raymarching all work — they render, animate, and match Shadertoy's appearance, with params exposed as node knobs. The Assistant can write or adapt them too. (Single-pass procedural shaders; image-channel inputs and multipass are not yet supported.)
Fixed
- The app always opens a window now — even when the graphics engine can't start. On some machines a graphics-driver crash during startup left the app running in the background with no window at all. The window now opens first and shows a clear error (with a crash report you can send) if the engine fails to initialize, instead of vanishing silently. (Seen on Windows with certain GPU drivers; updating the graphics driver resolves the underlying crash.)
0.1.25 — June 28, 2026
Added
- Transform multiple layers as a group — select 2+ layers and the viewport shows one bounding box that moves, scales, and rotates all of them together about the group center. Arrow keys also nudge the whole selection. Works on any layers — a Transform2D is auto-inserted where one is missing.
- Layer parenting + Null objects — a Parent column in the timeline lets a layer inherit another layer's (or a Null's) transform, so moving/animating the parent moves its children while each keeps its own offset. Parent chains work — an arm parented to an elbow parented to a shoulder inherits the whole chain. Moving a parent's anchor point re-pivots only that layer; its children hold their place. Multi-select to parent several layers at once. A New Null toolbar button adds a transform-only layer to parent a group to. Unparenting keeps the layer in place. (assumes default Pixels position mode; nulls are moved via Properties/arrow keys.)
- Text Animate — scalar offset fields + independent X/Y amplitude — the Offset Field input now accepts a scalar field as well as a vector field. A scalar field drives the per-glyph position offset on both axes from one sample (black = at rest, white = fully offset — same "black = none" convention as the Scale/Rotation/Opacity fields), and the field amplitude is now a vec2 (X/Y) instead of a single value — so you can zero one axis to push letters along the other (e.g. a noise/gradient scalar field driving only horizontal letter drift), or scale X and Y independently. Vector fields still drive the two axes separately. Existing projects are unchanged. Beta-user requested.
- Keep a matte source layer visible — a layer used as a track matte is normally hidden from the final composite (it only supplies the matte). An eye toggle on the matte source layer's own row in the timeline lets it also render while still acting as the matte (e.g. text matted by a rectangle you still want to see). Off by default (unchanged behavior); per-layer.
Changed
- Composition Settings & Export dialogs — input pass — the text/number fields no longer show Chromium's yellow autofill highlight or the native number-spinner arrows, and now use a quiet accent focus border consistent with the rest of the app (shared
.dialog-inputstyle + a reusableNumberField). The "Motion Blur" heading uses the standard engraved section-label style. - Composition Settings — resolution — Width and Height can be aspect-locked via a chain toggle (same icon as the Transform2D scale link); editing one dimension scales the other. The resolution presets are expanded and grouped — HD / UHD (720p, HD, 1440p, 4K UHD, 4K DCI), Vertical / Social (9:16, 4:5, 1:1), and Film (2.39:1).
- Export — format picker — the eleven export formats are now split into a Type selector (Video / Image Sequence / Animated / Audio) that narrows a second Format list, instead of one long flat menu.
Fixed
- Cmd/Ctrl +/− (and trackpad/Wacom pinch) no longer zoom the whole app — these used to scale the entire UI like a web page, and once engaged they broke the node-graph wheel (it scrolled instead of zooming). Browser/page zoom is now blocked at the source (menu zoom roles removed, pinch/visual zoom locked, ctrl+ wheel suppressed); the zoom hotkeys instead zoom the panel under the cursor (node graph, viewer, and all three timeline modes) as if you'd scrolled there. Wacom zoom gestures now zoom the panel under the cursor too, instead of the app.
- Generators & spatial effects no longer drift from unrelated nodes — Noise, Random, Voronoi, Gradient, Mosaic and UV Remap used to change appearance when a sibling branch in the same layer grew past the comp (e.g. raising Point Grid spacing feeding CloneToPoints, merged in alongside): the bigger branch enlarges the layer's internal render canvas (overscan), and these nodes were sizing their pattern/warp against that canvas rather than the composition — so the result stretched (anisotropically, in whichever axis grew) and shifted. They now anchor and scale to the composition region, so the output depends only on the node's own params — identical regardless of a sibling's overscan or the render quality (Draft/Preview/Full), and unchanged for existing projects (no-op when overscan is 1). (DistanceField, DrawAscii and Tile were already comp-correct.)
- Composition Settings is now truly modal — clicking outside the dialog (or a drag that starts in a field and releases outside it) no longer closes it and discards your edits. It stays open until you choose Save or Cancel; Escape cancels. The Export dialog got the same treatment.
- "Check for Updates" now shows what happened — the manual check used to surface nothing for the common outcomes, so it looked like it did nothing. It now reports every state — Checking…, You're up to date, Update available with live download progress, or an error — and the "Update ready" pill has a solid background so it's no longer easy to miss.
- Dope sheet names stay aligned to their tracks — the parameter-name list (left) and the keyframe tracks (right) now scroll together in the dope sheet, so a param's name always lines up with its row.
- Ease/interpolation menus flip up near the screen bottom — the right-click ease presets and interpolation menus (keyframe tracks and graph editor) now measure themselves and open upward when there isn't room below, instead of being clipped.
- Point Grid rows/columns are whole numbers — the Grid node's Rows and Columns fields now snap to integers (minimum 1) when typed or dragged, instead of accepting fractional values.
- Opacity sliders clamp to 0–1 — typing or dragging an opacity value can no longer push it above 1 or below 0. Field-driven opacity (via an input port) is unaffected and may still use any range.
0.1.24 — June 25, 2026
Added
- Shape fills can follow the shape — new DrawShape
Fill UVcontrol: set it toLocaland a connectedfill_source(gradient, image, field, or shader) moves, rotates, and scales with the shape instead of staying pinned to the comp. The default staysCanvas(comp-fixed, the prior behavior);Localis opt-in. The same option is available forstroke_sourceviaStroke UV. Works for every source type. - Text Animate glyph anchor (Baseline / Center) — a
Glyph Anchorcontrol sets where every letter rotates and scales from: the baseline (default) or the letter's vertical centre (e.g. scale each character from its middle). Selecting a Text Animate node now also shows a per-glyph gizmo in the viewport — a dashed box around each letter with a dot at its pivot — so you can see and choose the anchor; the dots read the engine's exact pivots, so they match the render. - Shape Morph supports Expose Channels — the "E" button on Shape Morph now reveals
MixandOffsetinput ports, so you can drive the morph from another node (a Time node, a Math expression, a field, etc.) instead of only keyframing it. The driven param shows a link indicator in Properties. - Drive per-letter text rotation from a field — Text Animate has a new
Rotation Fieldinput. Feed it a Gradient/Noise/any field to rotate each glyph by the field value (a C4D Plain-Effector–style per-character effector), with aField Rotation Amplitudecontrol for the degree range. - Transform2D anchor-point picker — a 9-point grid in Properties snaps the transform anchor (rotation/scale pivot) to the content's corners, edges, or centre in one click. Picking a preset keeps the content visually in place (position auto-compensates), exactly like dragging the anchor handle in the viewport. (Distinct from the "Pin To" picker, which positions the layer relative to the comp frame.)
- Anchor handle snapping — dragging the anchor crosshair in the viewport now snaps to the content's corners / edges / centre (the same 9 points the picker offers); the crosshair turns yellow while latched. Hold Cmd/Ctrl to drag freely.
- Rotate shape elements as a unit — Shape Attributes' Rotation target gains a Rotate Geometry toggle that bakes the rotation into the shape. Paths sharing a
glyphIndex(e.g. the bowl and counter of "o"/"a") rotate together, so TextToShape → Shape Attributes now actually rotates whole letters.
Changed
- Text Animate starts as a clean pass-through — the In animation now has an In Animation toggle (like Out), off by default, so a freshly-added node applies no entry offset/fade and the field-driven effector workflow (rotation/ scale/offset fields) works immediately with amplitudes front-and-centre. The Timing params (Target/Stagger/Order/Easing) — which only drive the staggered reveal — are hidden when both In and Out are off. Existing projects keep their In animation running.
- Transform2D "Anchored" position mode renamed to "Pinned" (and its "Position Anchor" edge picker to "Pin To") — clearer that it positions the layer against a comp-frame edge, separate from the Anchor Point rotation/scale pivot. Display rename only; existing projects are unaffected.
Fixed
- Thicken on a closed shape makes a proper donut — thickening a circle (or any closed path) now cuts the inner hole as intended instead of filling a solid disc. The outer and inner contours are tessellated together so the hole knocks out.
- Text Animate scale now resizes glyphs — per-glyph scale (the In/Out
Scaleand theScale Field) previously moved glyphs toward a point instead of resizing them. Glyphs now scale for real. The Scale Field is also now strength-based like the Rotation Field — field0= no change,1=Field Scale Target(default 0 = shrink away) — so the same gradient drives rotation and scale coherently (black = no effect, white = full). - Per-glyph rotation keeps the baseline — rotating characters used to pivot around each glyph's bounding-box centre, so the baseline drifted letter to letter. Rotation (and scale) now pivot on a baseline-anchored point, so the baseline stays put.
0.1.23 — June 23, 2026
Added
- Windows is here — Caddis now runs on Windows 10/11 (x64), signed and installable, with the full node/compositing engine, video import/export, and AI features at parity with macOS.
Changed
- Device-limit screen links to your account — when a license hits its 2-device limit, the screen now has a "Manage your devices" button that opens your account page so you can deactivate a device yourself — handy for freeing a slot (e.g. removing an old Mac) when activating Windows.
Fixed
- Dither no longer crashes on large compositions — applying Dither to a high-resolution comp could exceed an internal GPU buffer limit and crash the engine mid-render. Large comps now render through Dither cleanly.
0.1.22 — June 13, 2026
Added
- Motion tracking — a full planar + point tracking suite. Planar Track analyses a flat surface (a screen, a sign) and bakes its four corners per frame; Corner Pin warps any layer onto those corners for screen replacement. Track once, scrub instantly, and nudge a corner mid-clip to lay keyframed corrections on top of the bake (the Mocha "adjust track" model). Track Points tracks many features or blobs with stable IDs and feeds them straight into the Points pipeline (DrawPoints / PointTrail / CloneToPoints). All classical, pure-Rust, no cloud round-trip. The Track controls sit at the top of the Properties panel with a coverage bar and a single-frame stepper.
- AI video nodes — Generate Video (text-to-video, or image-to-video from a first frame, with an optional last frame for transitions), Generate Video Depth (temporally-consistent depth video), Remove Video Background (whole-clip AI matting to transparency), and Segment Video (text-prompted rotoscoping — name an object, get a tracked matte). Curated models per function with a fast/cheap pick plus premium tiers; cost scales with duration and is shown live before you spend. Generations run in the background and play back like any video clip.
- Point Trail now outputs points and fades — a
pointsoutput for dotted trails (via Draw Points), a Smooth mode that fits bezier curves through the samples, a tail Fade with an adjustable falloff, and Inherit Color to carry each source point's color down its trail.
Changed
- Point Trail is now fully deterministic — the trail at any frame is a pure function of the timeline, so scrubbing anywhere, looping, or exporting from any start frame all produce the exact same trail. Sequential playback stays fast (incremental), and the old background "Building trails" pre-roll pass is gone — it's no longer needed.
Fixed
- Point Trail no longer glitches on stutters or cache clears — trails were rebuilt from whatever happened to be evaluated, so a dropped frame or a cache clear could restart or shorten them. They now rebuild correctly from the source motion every time.
0.1.21 — June 11, 2026
Added
- File Cache can now cache geometry, not just renders — connect a points or shape source (a particle sim, a heavy procedural chain) straight into File Cache and bake: positions, IDs, and all per-point/per-path attributes are stored on disk per frame (compressed, typically 100–1000× smaller than caching the rendered pixels) and the upstream chain — including stateful simulations — is skipped entirely on playback. Downstream Draw nodes stay fully editable against the baked geometry. The node's ports switch type on connection, the same way Transform2D does.
Changed
- File Cache frames are ~5–20× smaller on disk — texture payloads are now byte-shuffled and zstd-compressed instead of raw float pixels (a 300-frame bake that used to take 37 GB lands in the low single-digit GBs for typical content, less for flat/graphic frames). Old uncompressed caches still read fine; re-bake to shrink them. Smaller files also mean proportionally faster cache playback.
- Cached frames are kept hot in memory — File Cache reads now land in an in-memory LRU (512 MB for textures, 128 MB for geometry), so scrubbing back and forth or looping over cached frames stops re-reading and re-decompressing from disk every single evaluation.
Fixed
- Baked File Cache frames no longer rescale on playback — a texture bake whose content extended past comp bounds (e.g. caching Draw Points after a point sim) was stored at the layer's overscan canvas size, but on read the pruned upstream chain made the engine size a smaller canvas and stretch the baked frame into it: the whole frame visibly shrank. Cache files now record their bake-time canvas, reads place content at 1:1 comp pixels (never a raw stretch), and a serving File Cache anchors the canvas size to the baked extent. Re-bake existing texture caches to pick up the recorded canvas; geometry caches are unaffected.
- Selecting a File Cache node no longer disables it — clicking the node (e.g. to watch its bake status) made the Details-panel inspection pull the entire upstream chain back into evaluation on every render, silently erasing the cache's performance win. Inspection now respects cache hits.
- Draw ASCII respects upstream transforms — a video or image scaled down through a Transform2D into Draw ASCII showed a blown-up center crop instead of the full picture: the overscan computation treated Draw ASCII as a pure generator and never followed its image input, so the source was cropped at comp bounds before the scale-down. The image branch is now walked like any other chain, and the ASCII grid is comp-aligned (cells are sized and anchored to the comp, continuing into the overscan margin) so cell size no longer drifts when the canvas grows.
- Closed the remaining overscan-crop gaps — the same "secondary input gets cropped at comp bounds" bug fixed for Draw Shape / Draw Text fill sources in 0.1.20 also affected every other per-pixel map input the overscan walk skipped. Now walked: Merge's second-and-later inputs, UV Remap's UV map, Blur's blur map, Lens Blur's depth map, Vector Motion Blur's flow input, Draw Glass's background and distortion inputs, and all four Channel Combine inputs. (Lens Blur's bokeh kernel is intentionally excluded — it's a self-contained lookup, not canvas-aligned.)
- Video sources now contribute to overscan after project reload — chain walks read video dimensions from a cache that was only populated when the editor probed a video on import. Probed video metadata is now mirrored into that cache on every render, so a reopened project gets correct overscan for video layers without re-importing.
- Image Sample's "Comp" coordinate space is origin-centered — with
coordinateSpace = Comp, samples were interpreted as top-left-origin comp pixels while every field consumer passes origin-centered coordinates ((0,0) = comp center, the project-wide convention). All samples landed shifted by half the comp, clamping the left/top half to the raster edge. "Comp" now recenters correctly; "Local" (the default) was always correct. - Engine no longer crashes on large field readbacks — wiring Image Sample or Optical Flow fields (e.g. Draw Field streamlines) in a comp whose canvas grew large (big comp × overscan) hard-crashed the engine: the raster→field CPU readback allocated a staging buffer sized to the full canvas, and wgpu treats exceeding its 256MB buffer limit as a fatal validation error. Field readbacks are now budgeted at 128MB — oversized canvases are downsampled on the GPU before readback (fields are smooth; consumers map coordinates through the raster's own dimensions, so this is visually transparent).
0.1.20 — June 9, 2026
Added
- OpenEXR sequence export — export a comp as a numbered
.exrsequence (Export → preset EXR Sequence). Frames are written as 16-bit half-float, linear scene-referred, ZIP-compressed RGBA — the engine's native working space, with no sRGB baked in — so they drop straight into Nuke / Resolve / comp pipelines. Complements the existing EXR/HDR input support.
Fixed
- Long video exports no longer time out — exports that took longer than ~10 minutes failed with "Engine request timeout" regardless of how well they were going. The export timeout is now a keep-alive watchdog: it resets on every per-frame progress event, so an export can run for any duration as long as frames keep rendering, and only fails on a genuine stall (no progress).
- Scaled-down fill/stroke sources are no longer cropped — feeding a large image or video through a Transform2D that scales it down into a Draw Shape or Draw Text fill (or stroke) source showed only the center crop, as if the scale hadn't applied. The overscan computation now follows the fill/stroke-source branches (like it already does for Mix and Matte inputs), so the full source is available before the scale-down — the whole image shows, scaled to fit.
0.1.19 — June 8, 2026
Fixed
- Large / custom resolutions no longer crash the engine — comps wider or taller than 8192 px (the old GPU texture cap) hard-crashed the render engine. Caddis now requests the GPU's real maximum (16384 on Apple Silicon) and clamps render targets to it, so a high-resolution comp renders cleanly instead of crashing. Comps beyond the hardware limit render at the capped size (and slightly lower effective DPR) rather than failing. Custom resolutions in Composition Settings now accept up to 16384 per side.
0.1.18 — June 8, 2026
Added
- Custom curve colors in the graph editor — click the color swatch next to a curve in the graph editor's parameter list to recolor it from the same palette used for layer/asset tags (or "Default" to restore the automatic color). The choice is saved with the project and is undoable. Default colors also now match the palette (X = red, Y = green, Z = blue), and a position split via Separate Dimensions keeps its X/Y curves red/green instead of falling back to yellow.
- Alt-click a parameter name to reset it to default — in the Properties panel, hold Alt (Option) and click a parameter's label to snap it back to the node's factory default. Works for numbers, vectors, colors, toggles, dropdowns, text, fonts, and file paths. Handy when you've experimented a value into a mess and just want the original back. (Driven parameters — fed by an edge — aren't resettable this way, since their value comes from the source.)
Ffits whatever you're pointing at — pressFto fit the panel under the cursor: the Viewer fits the canvas to the frame (same as its "Fit" zoom), the Node Graph frames all nodes, and the Timeline's Graph Editor fits the visible keyframes in both axes (time and value). Each panel fits independently, so it always does the right thing for where you are.
Changed
- Opening a project now always confirms before replacing the current one — double-clicking a
.caddisin Finder (or picking one from Open Recent) used to swap out your open project with no prompt whenever there were no unsaved changes. Now it asks first: Save / Don't Save / Cancel if you have unsaved changes, or a simple "Open …? This will replace the project currently open" otherwise. (No prompt on the launch screen, where there's nothing to replace.) - Audio Source can import video files — its file picker now offers an "Audio & Video" filter, so you can point an Audio Source node straight at a video (MP4, MOV, etc.) to use just its audio, without needing the video itself. (The engine already extracted audio from video containers — this removes the picker restriction that hid them.)
Fixed
- Gizmos and motion paths now render outside the comp bounds — the motion- path overlay (the curve + draggable position keyframes) and the shape-drawing previews (rectangle/circle/polygon/pen/text) were clipped at the canvas edge, so an object keyframed off-screen had no visible, editable path. They now draw into the full viewport like the other gizmos (matching the bounding-box, shape, and gradient gizmos), so you can see and adjust handles beyond the comp frame.
- Timeline zooms out to fit long comps — the timeline zoom was floored at 1 px/frame, so after increasing a composition's duration (e.g. to several thousand frames) you couldn't zoom out far enough to see the whole thing. The zoom-out floor now adapts to the comp duration and the timeline's width, and a Fit button next to the zoom controls snaps the view to the full comp in one click. The ruler also thins out its ticks when zoomed way out.
- Field Math "Power" no longer freezes the app — switching a Field Math node's blend to Power while it fed a Draw Field node could hang the engine (the canvas would stop updating and you'd have to restart). Fields are evaluated over positive and negative values, and raising a negative number to a fractional power (or zero to a negative power) produces "not a number"/infinity, which then drove the field visualizer into a near-infinite drawing loop. Power now does the math in a way that always yields a finite, direction-preserving result, and the field renderer can no longer be sent off to infinity. Other Field Math modes were unaffected.
0.1.17 — June 5, 2026
Added
- Numeric ease influence in the graph editor — with keyframe(s) selected, the graph editor footer shows In % / Out % boxes to set the easing influence (handle reach along time) numerically, like Resolve. Adjusting the value changes the handle's time-extent while preserving its shape; typing on a linear keyframe converts it to an ease. In is disabled on the first keyframe, Out on the last.
- Missing-font warning — when a project uses a font that isn't installed on your machine, the text silently rendered in a fallback before with no indication. Now an amber FONT badge appears on the node and a ⚠ marker on the font row in Properties, with a tooltip showing which font is missing and what it's being rendered with. (Fonts still aren't embedded in the project file — only the name is saved — so this flags the substitution rather than preventing it.)
Changed
- Shift constrains bezier handle dragging — holding Shift while dragging a keyframe's ease handle now locks it to the dominant axis (horizontal-only or vertical-only), matching the existing Shift axis-lock for keyframe dragging.
- Pen tool previews the actual curve — when the previous point has a bezier handle, the dashed rubber-band line to the next point now shows the curve that will be created instead of a straight line. (After a corner point it still previews straight, as before.)
Fixed
- Sign-in no longer hangs when the server can't be reached — if the request for a magic link failed at the network level (
fetch failed), the sign-in screen got stuck on "Sending…" forever with no error and no email. It now shows "Could not reach the server. Check your connection and try again." with a Try again button, so a transient blip is recoverable instead of a dead end. - Marquee select works in the speed graph — dragging a selection box in the graph editor's Speed mode now selects keyframes (it only worked in Value mode before). The box hit-tests against each keyframe's velocity, matching where the markers are drawn.
- Export now matches the composition's frame rate — exports were locked to 30fps regardless of the comp setting (e.g. a 60fps comp exported at 30). The export dialog was reading a non-existent
fpsfield and silently defaulting to 30; it now derives the rate from the comp like the rest of the app. - Node search now highlights the keyboard selection — in the node-graph add-node menu, arrowing through search results didn't visually highlight the selected item (because the menu opens under the cursor, which counted as "hovering" and suppressed the highlight). The highlight now follows the arrow keys and the mouse consistently.
- Audio files are selectable from the Audio Source node — its file picker was falling back to an images-only filter, so audio files showed greyed out in the open dialog. It now offers the audio formats (mp3, wav, aac, flac, ogg, aiff, m4a), matching the Assets panel.
- Font picker no longer gets cut off near the bottom of the screen — the font-family dropdown (Text, DrawAscii, etc.) now opens upward when there isn't room below and caps its height to the available space, so it's never clipped on smaller windows.
- The launch and sign-in screens can be dragged again — they had no titlebar, so on macOS there was nothing to grab to move the window. They now have a draggable titlebar strip (matching the editor's), with the traffic lights in it.
- The "Update ready" pill no longer overlaps the top bar — it's positioned below the toolbar so it's fully visible and clickable, including on smaller screens. The launch screen is also now responsive: it reserves room for the pill, centers when there's space, and scrolls instead of clipping when the window is short.
Removed
0.1.16 — June 5, 2026
Added
- Shape Morph node — interpolate ("morph") between two or more shapes with a single keyframeable
mix(0 = first input, 1 = second, 2 = third, … with fractional values blending the bracketing pair). Inputs don't need matching vertex counts — each is resampled to a common point count automatically. An Auto Align pass (on by default) matches winding direction and picks the lowest-travel start offset so the morph deforms in place instead of swirling through itself;Offsetnudges the correspondence for fine-tuning or spin.
Changed
- The "Update ready" pill is now fully clickable — clicking anywhere on it (not just the word "Restart") restarts and installs the update. It also gets a subtle accent outline so it's easier to notice.
0.1.15 — June 3, 2026
Added
- Trackpad pan & pinch-zoom in the Viewer and Node Graph — a two-finger swipe now pans the canvas and pinch zooms it (cursor-centered) in both the viewer and the node graph. Mouse behavior is unchanged: the scroll wheel still zooms at the same speed, and
Cmd/Ctrl+wheel remains an explicit zoom. - Trackpad gestures in the timeline (Keys & Graph) — a two-finger swipe now slides the timeline (and pans the value axis in the Graph Editor) instead of zooming, and pinch zooms the time axis. Previously both gestures zoomed. Mouse wheel still zooms as before.
- Maximize a panel with
`— press the backtick key to expand the panel under your cursor to fill the dock area (the top toolbar and status console stay put); press again to restore. Especially handy for the node graph. The panel keeps its state — zoom, scroll, selection — across maximize/restore.
Changed
- Repurposed the
`key — it now maximizes the hovered panel (see Added) instead of toggling the developer performance HUD. The HUD remains available via its toolbar button in dev builds.
0.1.14 — May 31, 2026
Added
- Anonymous usage analytics (opt-out) — Caddis now collects anonymous, aggregate product analytics (which features and nodes get used) to guide development. No session recording, no personal data beyond your account email, and no third-party scripts run in the app. Turn it off any time via Caddis → Share Anonymous Usage Data.
Fixed
- Easing presets on multiple keyframes — applying an easing preset to two or more selected keyframes now eases every segment correctly. Previously only the first keyframe of a selection became a bezier; the trailing keyframe stayed linear, so its incoming handle was discarded and the resulting curve was wrong.
0.1.13 — May 30, 2026
Added
- Linked nodes — paste with relative references, AE-style. Cmd+C on a node, Cmd+Shift+V in any graph (same or different comp) creates a linked instance: at eval time the engine mirrors the source's params / bypassed / expose-channels, while local edges and ports stay independent so the same Glow plugs into different upstream content in each layer. NodeBox grows a cyan pill below its bottom- right with a chain glyph ("Go to source") and a broken-chain glyph ("Unlink — snapshot source values into this node"). Source resolution walks the link chain with cycle detection (max 8 hops); when the source is missing the local values act as the snapshot fallback. NodeInspector renders source params (keyframe-resolved at the current frame) as read-only when the link resolves.
- Published nodes drawer — right-click any node body → "Publish as" registers it in the comp's
publishedNodesregistry. The bottom-left "Published" panel gains a Nodes section listing every published node across the project, each draggable into any graph (same or different comp) to drop a linked instance at the cursor. - Publish any param as a
ValueSourcetarget — right-click a Properties param row → "Publish as". The engine pre-resolves param publishes (keyframe-aware) into the same resolver used by port publishes, so consumers don't care whether the source is a port or a param. ValueSource auto-infers its output port type from the source param'sParamValuetype (scalar / vec2 / vec3 / color / string; boolean flows as Scalar 0/1). Amber 3px bar marks the published row, matching the existing port-publish accent. - Cross-comp layer copy/paste with Cmd+C / Cmd+V — copies the layer's full graph + node positions + groups + token links + color tag in a single undo entry. Cross-layer matte refs survive when both layers are copied together; otherwise they're cleared.
- Tapered stroke on DrawShape. Four scalar params (
taperStart,taperEnd,taperStartLength,taperEndLength) shape the stroke width along each path — AE-style. Defaults are uniform; touch them for leaves, calligraphy, comet tails, etc. Per-vertexstrokeWidthattribute on the upstream shape (write it with ShapeAttributes Custom target) overrides the params for full programmatic control. All four params expose as scalar inputs for animation. - Dashed stroke expose channels on DrawShape —
dash_in,gap_in,dashOffset_in. AnimatedashOffsetfor marching-ants / running- stitch effects. - String as a first-class port type, with a new String source node (keyframeable text, Hold-interpolated), a MergeText node that concatenates strings with a separator (same multi-input pattern as MergeShapes / MergePoints), and a
content_inport on Text that overrides the literaltextparam. The Text node's owntextparam is keyframeable too — animate captions, lyric typography, or AI-prompt assembly directly. - MergePoints node — concatenates point streams with conservative attribute merging (attribute only survives when present on every input with the same type, so common attrs like
opacity/scaledon't get zero-poisoned). Always emits asourceIndexper-point attribute for downstream routing. - PathDelete node — sub-path-level filter, companion to PointDelete at the vertex level.
- Thicken node — wraps an open path as a closed shape with caps; closed paths emit donut topology.
- Per-path delay convention for animated traversal. TrimPath and ShapeAlongPath both accept a
delayAttributeparam that reads a per-path scalar attribute and staggers timing per path — set it via ShapeAttributes Index/Custom on a multi-path shape for "branch growing" effects. - Expose-channel inputs surfaced across the node graph. Many nodes had the engine plumbing already but were missing from the E-button allowlist (or had no helper yet). New / restored toggles on: Voronoi, Vortex, RadialForce, PointsField, Checkerboard, Stripes, Remap, Threshold, ColorCorrect, DrawAscii, Crop, plus all nine Effects nodes (Mosaic, Posterize, Vignette, EdgeDetect, Sharpen, Grain, ChromaticAberration, Glow, DropShadow). New expose channels on Dither (
levels,cycle), VectorMotionBlur (strength,phase), Oscillate (offset), PointScatter (count,seed), PointsAlongPath (always-oncount_in). - Grid pattern-aware expose ports. Each Grid pattern (Grid / Radial / Hex / Sunflower / Jittered / Concentric) now exposes only the channels it actually consumes, instead of showing the same static four. Switching pattern swaps ports + prunes orphan edges.
- Debug node actually inspects data. ScalarField shows source label + center value + min/max/mean from a sparse sample grid. VectorField shows magnitude + direction angle + magnitude range. ColorField shows a center swatch + alpha. Previously every field just read "Scalar Field" / "Vector Field" / "No input" — now you can see the values. String / Audio / Text cases that previously fell through to "No input" also render properly.
- FileCache Properties redesigned. New Location row (full cache path + Reveal in Finder button), Status, Bake, Progress, and Last rows all use the standard param-row grid. The status badge ("…F BAKED" / "EMPTY") now sits as a pill above the node header instead of crowding the title.
- DrawGlass NO-BG badge sits above the node header (same pill style as the new FileCache badge) so it doesn't overlap the E / bypass buttons.
- Open Source Licenses screen (Help → Open Source Licenses…) + bundled GPLv2 / LGPLv2.1 / FFmpeg LICENSE.md / THIRD-PARTY.md texts for the bundled FFmpeg. Build provenance and a corresponding- source pointer ship with every release.
- Drag-to-scrub on numeric parameter inputs. Click and drag horizontally on any numeric parameter in the Properties panel to adjust the value. Modifier keys scale the per-pixel increment: Shift ×10, Ctrl/Cmd ×0.1, Alt ×0.01. A small "sensitivity ladder" tooltip floats above the input showing all four levels with the active one highlighted in amber. Click without dragging still focuses the field and selects its contents for typing. Escape mid-drag reverts. The whole drag coalesces into a single undo entry.
- Arithmetic expressions in numeric parameter inputs. Type expressions like
200 / 3,(1920 - 100) * 0.5, or360 / 8into any numeric parameter and they resolve to a number on commit (Enter / Tab / blur). Supports+ - * / %and parentheses with standard precedence, plus unary+ -. - Create a composition from a dropped asset. Dragging a video, image, vector, or audio asset onto the Compositions section of the Assets panel creates a new comp sized + timed to match the source media, with the asset already wired up as a layer. Video comps inherit the source's exact pixel dimensions, frame rate (NTSC-aware: 23.976 → 24000/1001), and duration. Image / vector comps inherit pixel dimensions and default to a 5-second duration at 30 fps.
- Frame rate mismatch warning on VideoSource nodes. When a video clip's frame rate doesn't match the active comp's frame rate, the Properties panel shows a small amber warning above the node's params so it's obvious that playback may judder.
Changed
- Performance HUD is dev-only. The viewport perf dashboard (toolbar button, overlay, backtick shortcut) is gated by
import.meta.env.DEVso production builds tree-shake the dashboard out entirely. The underlyingusePerfStorestill tracks frame metrics for non-UI consumers. - EditableShape gizmo: AE-style multi-point selection. Click a point to select it (unchanged), Cmd/Ctrl-click another point to add it to the selection (was already implemented but undocumented), and double-click a point to select every point on the same sub-path. The corner ↔ smooth toggle that used to live on double- click stays accessible via Alt+click, matching the pen-tool convert affordance. Cmd+A still selects every point across every sub-path, so the new gesture sits between "one point" and "all points" for letter outlines or multi-loop shapes.
- EditableShape: forgiving bezier-handle hit target. The 3px tangent dots now have a 9px invisible click ring, so grabbing a handle no longer requires pixel-perfect aim. When a handle sits on top of the path (common for shallow curves) it also takes priority over the segment-insert preview — previously the white "add point here" dot would steal the click and drop a new vertex on top of the handle.
- EditableShape: marquee select. Drag in empty space inside the viewport to lasso every point inside the rect. Shift- or Cmd-drag extends the existing selection instead of replacing it. A plain click (no drag) still deselects, matching the old behavior. Middle-mouse and right-click pass through the gizmo, so the Viewer's pan + context-menu shortcuts keep working in edit mode.
- Graph editor: forgiving tangent handle hit target. The 3px bezier tangent dots in the timeline graph editor now have the same 9px invisible click ring as the EditableShape gizmo. No more chasing a single pixel to ease a curve.
- Graph editor: unified scope filter (Node / Layer / Comp). The old two-toggle header (
Layers: Active/All+Params: Single/ Node/All) is gone — replaced with a single Node / Layer / Comp segmented control matching the dope sheet. Node lists only animated params on the selected node(s), Layer lists every animated param on the active layer, Comp lists every animated param across the whole composition. Scope state persists per timeline mode so a wide graph view can sit alongside a node- focused dope sheet. - Graph editor: focused selection + auto-frame. Clicking a param row now focuses that row — the previous selection drops away (no more reaching for the tiny eye bubble to remove it). Clicking the same row again toggles it off. Shift- or Cmd/Ctrl-click extends the selection so you can build a multi-curve view. After every change the value viewport vertically re-frames to fit every visible curve (1.2x padding), so the F-hotkey is now optional rather than required. Multi-curve views frame to the widest range.
- Graph editor: every visible curve is editable. Curves no longer dim to 40% when they aren't the active param — everything in the multi-select renders at full opacity, every curve gets tangent handles for its selected bezier keyframes, and marquee sweeps across every visible curve at once. So you can Shift-click position + scale, marquee their keyframes, hit an ease preset, and the easing lands on both at the same time. The active row still gets a slightly thicker stroke so the panel-highlighted curve stays easy to find.
- Graph editor: Shift now locks to the dominant drag axis. Previously Shift always zeroed the value delta (frame-only). Now a mostly-horizontal drag locks value (frame-only), a mostly-vertical drag locks frame (value-only) — so you can hold Shift to nudge a keyframe's value without worrying about it slipping a frame sideways. Decision is made from raw pixel deltas, so it tracks the cursor's actual direction.
- Graph editor: drag readout + Shift snap to whole number. A small floating tooltip follows the cursor while you drag a keyframe, showing the target frame and value live. When Shift locks the drag vertically, the value snaps to the nearest integer (pivoted on the anchor keyframe so multi-select drags preserve relative offsets) — so coaxing a position from 1 → 8 or 100 → 250 lands on clean numbers without typing.
- Graph editor: AE-style selection bounding box. Marquee or Cmd-click two-or-more keyframes (across one curve or many) and a thin dashed accent box appears around them. Drag the left or right edge to scale time around the opposite frame; drag the top or bottom edge to scale values around the opposite value. Multi-component params (Position vec2, Scale vec2, Color vec3/4) scale every visible component in lockstep, not just the first axis. Each scale is one undo step. Hidden for single-key selections (the keyframe itself is already a draggable handle).
- Transform2D: Separate Dimensions. A compact split / merge icon button sits in the vec2 row next to the X/Y link toggle on Position / Scale / Anchor. Clicking it spawns a MakeVector pre-seeded with the current value, wires it to the matching
*_inport, and migrates any existing vec2 keyframes into per-axis scalar keyframes so prior animation survives the toggle. From there, X and Y become independent — keyframe a quick horizontal bounce without disturbing vertical motion, ease them on different curves in the graph editor, etc. Clicking the same button while separated pulls the per-axis keyframes back into one vec2 param (interpolation degrades to linear on merge since the two axes can disagree). The MakeVector node stays in the graph. Each toggle is one undo step. - Gizmo writes + reads follow the wire. When T2D's
position_in/scale_in/rotation_in/anchor_inis driven by an upstream MakeVector or vec2 Constant, both the drag-write and the gizmo's at-rest baseline now resolve to that upstream node — drags write to MakeVector x/y (instead of T2D's silently-overridden local params), and the box stays locked to the rendered position the whole time (it used to snap to (0, 0) the instant a drag started because the local position param read 0). - Gizmo stays anchored to a downstream T2D. Selecting a MakeVector that drives a T2D's spatial port keeps the bounding-box gizmo visible — so you can keyframe X / Y on the MakeVector while still nudging the layer spatially with the box. Previously the gizmo disappeared the moment the selection moved off the T2D itself.
- Motion path overlay reflects driven motion. When position_in is wired (e.g. via Separate Dimensions), the motion path's shape is sampled per-frame from MakeVector's X + Y so the rendered visual exactly matches what the engine outputs (no fake spatial bezier overlay). Selecting either the T2D or the driving MakeVector keeps the motion path visible — the overlay walks forward one hop to find the downstream T2D. The keyframe diamonds toggle filled/hollow to show curved vs linear motion. Alt-click a keyframe toggles bezier interpolation on both MakeVector axes at that frame, so curves come from real per-axis scalar interpolation (fine-tune the bezier handles in the graph editor on each axis). Drag a keyframe to reposition writes to MakeVector x/y. Spatial 2D tangent handles aren't shown in driven mode — they don't apply to independent per-axis interpolation.
- Default Text alignment is now Center (was Left). Text-tool box- text drag still forces Left for paragraph layout — point-text / graph-spawned Text starts centered, which is what most motion-design use cases want.
- TextAnimate got a polish pass:
In Offset X/In Offset Ycollapsed into a singleIn Offsetvec2 (matches theoffset_invec2 expose port); same foroutOffset. The Out section is gated behind a new Out Animation boolean toggle, hiding the six follow-up rows by default so the panel reads cleaner.seedonly shows whenorder = Random. Engine reads the new vec2 first and falls back to the old scalar params for unmigrated projects. - Font / Weight / Style rows in the Properties panel now have the 16px keyframe-diamond gutter, so their labels line up with the rest of the rows.
- Drag sensitivity for [0,1]-ish params. Params whose names contain
opacity,progress,mix,evolution,contrast,strength,threshold,anchor, orscale(case-insensitive substring — catchesfillOpacity,strokeOpacity,fromOpacity,outFromScale, etc.) drag atstep=0.01instead of0.1. A 100- pixel drag now traverses the full [0,1] range cleanly. - NodeBox title truncates with ellipsis above 18 uppercase chars so long names like "Chromatic Aberration" don't bleed into the E button. Full label still shows on hover.
- Properties panel: the keyframe diamond (and driven-by link icon) now lives in a fixed-width column at the start of each param row instead of trailing the value. Diamonds stack vertically across the panel regardless of label or value width, making it easy to scan which params are keyframed. Applies to scalar / vec2 / vec3 / color / boolean / string / path rows, the Levels widget, and the Gradient / Curve ramp section headers.
- Node context menu: reorganized the ~110 nodes into 14 categories oriented around source / operation pairs instead of the old action- heavy buckets. New categories are Source, Shapes, Shape Ops, Points, Point Ops, Text, Fields, Effects, Color, Composite, Render, Math, Time, Utility. The 28-node "Effects" mega-bucket is gone — image effects now split into Effects (filters), Color (grading), and Composite (multi-input). Per-node docs in
docs/nodes/were updated to match. New docs added forAudioSource,MergeShapes, andNote(which previously had no docs).
Fixed
- Node bypass passthrough is now type-safe for multi-output and shape-typed nodes. The old logic walked a hardcoded input-port allowlist (
in / bg / fg / source / a) and always wrote toout, with a ColorSplit special case that wrote transparent to its r/g/ b/a outputs — which is why bypassing ColorSplit (and similar shape nodes like ShapeDeform / ResampleShape / SubdivideShape) blanked everything downstream. New behavior walks declared input ports, finds the first connected input whose upstream type matches a declared output type, and fans the value out to every matching output port. Type-conversion nodes (ShapeToPoints, BreakVector, DrawShape) fall back to a transparent texture as before. - Cross-comp link cache coherence.
diff_projectnow marks comps containing linked-node consumers dirty when their source layer changes. Without this, edits to a linked Glow's source didn't bust the consumer comp's frame cache and pre-edit pixels lingered. - Graph editor: keyframe + tangent drags are now one undo step. Both drag flows used
temporal.pause()/resume()to wrap their mutations, which suppresses tracking entirely instead of batching it — so the drag's edits never entered history, andcmd+zafter "ease → drag a value" reverted the ease and swallowed the drag in a single step. Now snapshots before pausing andpushUndoEntryon pointer-up, so each drag is its own history entry. - Curves: untouched curve no longer darkens footage. A half-texel- offset in the 256-bin LUT sampling biased bilinear interpolation toward the lower bin, compounding ~0.001 per stage across master + per-channel. Identity curve is now true identity at every stage.
- ColorCombine: overscan walk no longer dead-ends. The chain walk follows
PRIMARY_INPUT_PORTSto step backward through the graph, but ColorCombine's inputs (r_in / g_in / b_in / a_in) weren't in the list. Upstream content larger than comp was cropped at canvas bounds before reaching ColorCombine. Per-node-type primary port lookup added so the walk continues correctly. - Crop → Tile bounds. Crop was treated as a passthrough in the content-bounds walker, so Tile saw the full upstream image and tiled past the crop region. Crop is now a content-bounds terminal that intersects upstream bounds with its rect. Keying also added to the same walker's passthrough list (chains like
Crop → Keying → Tilenow resolve correctly). - Crop gizmo drag silently failed when clicking the central edge handle — pointer-capture routed events to an element with no move/up listeners. Move/up handlers lifted to the outer SVG group so any captured pointer event reaches the drag handler.
- Dot node pre-pass passthrough. Chains like
Noise → Dot → PointDeform.field_inlost the ScalarField during the pre-pass (AABB / overscan path), and the field consumer fell back to its default behavior. Added aDot | Debugpassthrough arm to the pre-pass for every CPU-side NodeOutput variant. - FileCache now actually writes to the project's cache folder. The initial
loadProjectran beforesetProjectFilepopulatedprojectDirPath, so the engine storedNoneforproject_dirand FileCache fell back to the OS temp dir. Subsequent syncs only fired on project-store changes, so opening a saved project and baking-without-edits never updated it. Fixed by injecting the resolved path before sending and subscribing toprojectDirPathin the sync hook. - FileCache Reveal works on an empty cache.
shell.openPathreturned an error when the leaf directory didn't exist; reveal nowmkdir -p's the path first. - ResampleShape bezier output no longer overshoots at endpoints — switched to centripetal Catmull-Rom (Yuksel et al. 2009) for smooth-but-non-overshooting tangents on multi-anchor paths.
- Export dialog: "Half" resolution no longer fails — the engine renders at its configured quality level and ffmpeg now scales the piped frames to the requested output dimensions, so input and output sizes can differ.
- Export dialog: "Work Area" range now actually maps to the timeline work area (previously silently fell back to full duration because of a tuple vs.
{start, end}shape mismatch). - Export dialog: custom frame range / width / height / CRF / GOP inputs can now be fully cleared while editing without snapping back to 0, and no longer show Chromium's yellow autofill highlight.
Removed
0.1.12 — May 23, 2026
First version with a maintained changelog. Prior releases (0.1.0 – 0.1.11) are listed on the GitHub releases page but were not logged in detail.
Highlights since 0.1.0:
- Auto-update via electron-updater + GitHub Releases
- Sentry crash reporting with symbolicated source maps
- Code-signed + notarized arm64 builds via CI
- Stable version-less download URL for marketing/welcome emails
- macOS file associations for
.caddisand.cadrecipewith custom icons