Materializr
開源參數式 3D CAD 給創客 — 受限草圖、實體建模、螺紋、SVG 與文字雕刻、STL/STEP 匯出。Android、iPad、Linux、Mac、Windows。Vibe 編碼。
AI 翻譯
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證AnmatedSVG editor
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗(Formerly Keyline.)
A browser-based SVG animation editor. You draw or import artwork, animate it on a keyframe timeline, and export one self-contained animated SVG (or any frame of it as a picture: see Export › Image). The SVG plays with a small JavaScript player (built in, or one shared player for pages with many animations: see The external player) or with pure CSS.
npm install
npm run dev # http://localhost:5173 with hot reload
npm test # unit tests (Vitest)
npm run typecheck # TypeScript, no emit
npm run build # type-check + production build into dist/
npm run preview # serve dist/ locally
tests/browser/run.sh # browser tests (Playwright for Python), see tests/browser/README.md
Requires Node 18 or newer (developed on Node 22).
index.html App shell: panels, dialogs, menus (static markup)
src/
main.ts Entry point: loads CSS, then runs every module's init() in order
styles/app.css All styles and theme tokens (light and dark)
runtime/engine.ts The player that ships inside every exported SVG (typed, dependency-free)
runtime/boot.ts Starts an SVG exported with the external player (JSON + trigger)
runtime/scrub.ts Linked to scroll: the animation follows the page's scroll (self-contained)
runtime/player.ts The external player file (svgcinema-player on npm)
core/
constants.ts Helpers ($, byId, rounding, escaping), property tables, icons, tool list
state.ts Editor state: the open document, selection, assets, id counters
dom.ts References to the main DOM elements
history.ts Undo/redo snapshots, setProp/toggleKey, selection, tool switching
keyframes.ts Keyframe clipboard, reverse, distribute, nudge, context menu
layer-ops.ts Convert to path (text to shapes), morph into another shape
select-same.ts Edit › Select same (fill, stroke, stroke width, font, kind)
presets.ts Animation presets and how they slot into existing keyframes
symbols.ts Symbols: instances sharing their contents, kept in step after each edit
playback.ts Editor preview playback loop
model/
tree.ts Layer hierarchy (groups, bones), parent matrices
filters.ts Layer filters (blur, drop shadow) with animatable parameters
pins.ts Pin property keys and labels (Pin tool)
mesh.ts The triangle mesh pins bend images, text and groups through
draw-order.ts A rig's own draw order (artwork drawn apart from its bones)
sample.ts The sample scene
geometry/
nodes.ts Path nodes, shape builders, splitting/equalizing for morphs
parse-path.ts SVG path-data parser (all commands including arcs)
render/
markup.ts Layer → SVG markup (shared by the editor canvas and the exporter)
text-layout.ts Text line breaking ("Keep width" wrapping), measuring, direction, case, RTL order
stage.ts Canvas render, fit, per-frame update
overlay.ts Selection box, handles, motion paths, nodes, pen preview
mesh.ts Builds and refreshes meshes from the canvas, measures meshed layers
tools/
stage-input.ts Canvas pointer handling
bones.ts Bone tool, forward kinematics, inverse kinematics (CCD)
pins.ts Pin tool and Edit pins tool
edit-tools.ts Move/scale/rotate/origin drags, text creation, node editing
pen.ts Pen, add/remove node, pencil; path hit-testing
shapes.ts Ellipse, rectangle, polygon, star, line
hand.ts Pan and zoom (hand tool, Space, wheel)
ui/ Panels and controls
layers-panel.ts timeline.ts inspector.ts easing.ts
tools-rail.ts (tool groups and flyouts) theme.ts (light/dark) topbar.ts file-menu.ts keyboard.ts
props-panel.ts (inspector sections) align.ts (align bar, Set motion path) font-picker.ts (the font list) multi.ts (editing several layers)
presets-panel.ts (the Presets window) onion.ts (onion skin) graph.ts (graph editor)
symbols-ui.ts (editing a symbol, Assets ▸ Symbols, the Instance section)
io/ Files
assets.ts Image import (incl. TIFF/HEIC conversion), IndexedDB, asset panel
svg-import.ts Open an SVG as a project / add one to the project
svg-convert.ts SVG elements → editable layers (hierarchy, names, paint, text, masks)
svg-paint.ts Style cascade, colours, transforms, gradients for the importer
embed.ts The editable project inside an exported SVG (read back)
svgator.ts SVGator animations → keyframes (player data, settings)
css-anim.ts CSS keyframe animations (SVGator CSS exports and others) → the same
zip.ts Minimal zip writer/reader and the .SVGcinema builder
save.ts open.ts guard.ts file-state.ts Save/Save as, open, unsaved-changes prompt
recent.ts Open recent (file handles, or stored copies)
export/
build.ts Builds the exported SVG (JS player or CSS keyframes)
dialog.ts Export dialog controls
sections.ts Export dialog sections: collapse, remembered, summaries
snippets.ts Embed code for HTML, React, Next.js, Vue, Angular, Svelte
size.ts Output size (responsive/fixed, W/H/%)
images.ts Image optimization (resize for output size, format, quality)
kind.ts The Export window's picker: Animated SVG, Image…
frames.ts Still frames of the animation (from the JS export, out of sight)
still.ts Export › Image: one frame as PNG, JPEG, WebP, AVIF, JPEG XL or SVG
video.ts Export › Video: MP4, WebM or PNG frames (WebCodecs), progress and Cancel
mux.ts The MP4 and WebM files (and av1C from an AV1 stream), written by hand
anim.ts Export › GIF & Animated WebP: settings, the comparison, the set
scrub-preview.ts The Linked to scroll preview: a test page that scrolls
anim.worker.ts Makes the GIFs and Animated WebPs off the main thread
gif.ts The GIF encoder: palette, dithering, lossy LZW, changed pixels only
webp-anim.ts The Animated WebP file from still WebP frames
encoder.worker.ts WebP, AVIF and JPEG XL encoders (WebAssembly, in a worker)
app/boot.ts Startup: restore the last project, assets and file handle
tests/ Vitest: runtime player and easing, zip container, path geometry, export output
site/ The website (home, features, design agent, import, export, downloads, licence), see site/README.md
The runtime is embedded by serialising a function. export/build.ts writes CinEngine.toString() into each exported SVG.
src/runtime/engine.ts must not import anything or touch module scope.tests/engine.test.ts rebuilds the function from its source text and runs it.Modules have no side effects on import. Each module only defines functions and constants. Event listeners and other setup live in an exported init(), and src/main.ts calls the init() functions in a fixed order. This keeps the circular imports between UI modules safe.
Shared mutable state uses setters. ES module imports are read-only bindings, so replacing the open document goes through setDoc() and the canvas engine through setEng(). Other examples are setIdc(), nextAid() and setLastExport(). Reading doc anywhere always gives the current document.
The editor canvas renders the exact SVG that gets exported. render/markup.ts produces markup for both, and the canvas is animated by the same runtime that ships in the file. What you see while editing is what you get.
The editor opens with a sample scene (also under File → New → Open sample scene). It doubles as a tour of what SVGcinema can animate; select a layer to see how it's done:
tests/sample.test.ts checks the scene keeps showing all of these.
| Tool | Key | Notes |
|---|---|---|
| Select | V | Move, scale, rotate; Ctrl+click picks inside groups |
| Edit nodes | A | Drag nodes and handles; double-click toggles smooth/corner |
| Pen group | Hold or right-click the button for the flyout | |
| Pen | P | Click = corner, drag = curve. While dragging: Alt breaks the handle, Shift snaps to 45°, Space moves the point. Click the first point to close; click the last point to drop its handle. Enter / Esc / Ctrl+click ends, Backspace removes the last point. On the selected path: hover a segment to add a node, a node to delete it, Alt+node to convert it, an open end to continue the path |
| Add node | + | Click a segment of the selected path (rectangles and ellipses convert to paths) |
| Remove node | − | Click a node of the selected path |
| Convert point | ⇧C | Click a point: corner. Drag a point: smooth handles (Shift 45°). Drag a handle: moves it alone (cusp). Drag a segment: reshapes the curve. With the Pen, Alt does the same on points and handles |
| Pencil | N | Freehand; simplified and smoothed. End near the start to close |
| Shapes group | ||
| Ellipse | O | Shift = circle, Alt = from the centre |
| Rectangle | M | Shift = square, Alt = from the centre |
| Polygon | Drag from the centre; ↑/↓ while dragging changes the sides; Shift keeps it upright | |
| Star | As polygon; ↑/↓ changes the points | |
| Line | L | Shift = 45° steps, Alt = from the centre |
| Text | T | |
| Bone group | Hold or right-click the button for the flyout | |
| Bone tool | B | Drag to draw a bone; start on a bone tip to chain a child bone. New bones get a Rotate keyframe at 0 s holding the rest pose, so dragging a bone (or its tip, for IK) animates it. A bone's Scale works in the bone's own direction: W stretches along the bone, H across it |
| Edit bone | E | Edits the rest pose; keyframes stay as they are, and the bones show their rest pose while the tool is on (a chip says so). Drag a tip to change the bone's length and angle: bones chained on that tip follow it while their own tips stay put, and attached artwork doesn't move. Drag a joint to move it (a joint on its parent's tip moves that tip; Alt pulls the joint off it). Drag the bone to move it. Ctrl while dragging a tip turns the attached artwork with the bone. Shift snaps the angle to 15°; a tip snaps onto other bones' joints and tips |
Drawing tools stay active after each shape, as in Adobe apps. Press V or Esc to go back to Select.
Main menu (☰, left of the logo): New project, Open, Open recent ▸ · Edit ▸ (undo, redo, cut, copy, paste, paste in place, duplicate, delete, select all, deselect all; keyframes when keyframes are selected; Select same ▸ fill, stroke, stroke width, font or kind of layer: every visible, unlocked layer sharing that value with the selected one, compared at the playhead, inside groups too) · Element ▸ (the right-click menu's layer commands: group, ungroup, delete, center origin, motion path, hyperlink, convert to path, export selection, booleans, masks, symbols, align, arrange, hide, lock, rotate, flip) · Animate ▸ (Presets…, then the properties the selection can animate, with their ⇧ keys) · Save, Save as, Import files, Export · View ▸ (zoom, rulers, guides, grids, snapping, bones, pins, onion skin, collapse sections, dark theme; ticks show what is on) · Help ▸ (keyboard shortcuts, open the sample scene, spell check (on by default; it uses the browser's own spell check, which the page can't switch on, so turning it on reminds you where the browser's setting is), the website when the editor runs from the site's editor/ folder, and About SVGcinema: the version from package.json, the build's commit and when it was built (date and time, in your time zone), the copyright and licence from LICENSE in short and in full, and the open-source parts). Submenus open on hover or click and follow the selection (one with nothing to do greys out and opens nothing), deeper levels too (Element › Set motion path ›); a submenu stays inside the window, moving up or opening to the left when it must, and scrolls only when it is taller than the window; it is placed against the window, so even then the next level flies out beside it. A submenu stays open until the pointer settles on another item of the same list (a short delay), so crossing the gap, a scrollbar or a corner of a neighbouring item never closes it. The arrow keys, Enter and Esc work throughout. Every item shows its shortcut.
Selecting on the canvas: click a layer (the outermost one; Ctrl+click picks inside a group, Shift+click adds or removes). Drag from an empty spot for a marquee: every top-level layer it touches is selected when you let go (each gets a dashed outline while you drag); Shift adds them to the selection or takes them out, Ctrl picks the shapes inside groups. A click on an empty spot deselects. Alt+drag a layer drags a copy, leaving the original (one undo step). Ctrl+A selects every top-level layer that is shown and unlocked (every keyframe while you work in the timeline); Ctrl+Shift+A deselects.
Keyboard shortcuts (F1, the keyboard button in the top bar, or Help › Keyboard shortcuts) lists every shortcut in five tabs that follow the main menu, each in titled sections: General (File, Edit, Help), Layers (Select, Organise, Transform, Colour), Tools (Tools, Drawing, Bones and pins), Animation (Playback, Keyframes, Easing, Animate a property) and View (Zoom, Pan, Panels, Show and hide). The search box looks across all of them and says where each hit lives; the tool and Animate keys come from the real bindings, and on a Mac Ctrl and Alt show as ⌘ and ⌥. Letter shortcuts go by the physical key, so they work on any keyboard layout (Ctrl+Z is undo with a Hebrew or Russian layout too). Besides the ones below: Ctrl+H hides or shows the selection, Ctrl+↑ / ↓ bring forward / send backward (with Shift: to the front / back), Ctrl+X cuts the selected keyframes, Shift+Space stops and goes back to the start, I / Alt+I pick the fill / stroke colour from the screen (browsers with an eyedropper), / closes or opens every inspector section, Alt+G turns the front grid on or off, Alt+O the onion skin, and Ctrl+scroll over the timeline zooms it around the pointer.
The interface's own text (labels, menus, the layer list) can't be highlighted, so a stray drag or Select all never paints it; fields, text you type and code views can be selected as usual.
Zoom: click the percentage at the bottom of the work area for a menu with a slider (2%–3200%), a typed value, Fit selection (2), Fit (1), Zoom 100% (0), Zoom in (Ctrl +) and Zoom out (Ctrl −). The − / + buttons beside it step the zoom; Ctrl+wheel zooms at the pointer.
Layers panel tree: dotted lines join each group to its children, so nesting is easy to follow. Search (above the tree) shows the layers whose name contains what you type, inside their groups, with the match marked and counted; Enter selects every match, ↓ moves into the list, Esc clears it. Double-click a layer's name to rename it (Enter keeps the new name, Esc cancels); the same works on a layer's name in the timeline. Layers that a mask or clip folder applies to show a small hook before their icon (hover it for which folder), and the folder itself carries a mask badge.
Layers panel actions grey out when they don't apply:
Light and dark theme: the editor follows the system setting until you pick one with the half-circle button in the top bar, right of Keyboard shortcuts; the choice is remembered. Numbers use the UI font with tabular digits, so zeros stay open and columns still line up.
Work area: the artboard shows the project name and canvas size above its top-left corner. The ? at the bottom left shows the current tool's tips on hover (or when focused); with rulers on it moves clear of the left ruler. The timeline shows the current time and the total length (0:01.50 / 0:03.00), and the playhead carries a time flag in the ruler.
Inspector (properties of the selected layer):
Header: the layer's name (click to rename) and type.
Sections open and close: click a section's heading (or its chevron). Closed sections stay closed for every layer and after a reload.
Hyperlink (the last section; not on bones): a link address, and once there is one, whether it opens in a new tab or the same tab. Clicking the layer in the exported SVG opens it; web addresses (https:// is added when missing), mailto:, tel: and #anchors only. Clearing the field removes the link. Element › Hyperlink… edits the same link.
Help as tooltips: a section's explanation sits behind an ⓘ at the end of its title (Pins, Motion path, Morph, Star / Polygon, Line, Group, Instance, Bone, several layers), of a sub-heading (Trim path) or of a label (Mask by, Time offset, Key whole rig, Own draw order, Inherit scale, a bone's Scale); hover it, or focus it with Tab. An imported element that SVGcinema kept as it is shows a warning saying what it is and what can still change.
Order: a symbol instance's Instance section first, then the layer's own section (its shape: Rectangle, Ellipse, Path, Polygon / Star, Line; or Text, Image, Group, Imported element, Bone), then Transforms, Pins, Motion path, Text appearance, Fill & stroke, Appearance and Filters, with Morph and Rig last. Sections a layer doesn't have are left out.
Keyframe toggles: every animatable property has its own row and its own ◆, so colour and opacity, for example, animate separately. The ◆ shows three states, explained in the Transforms heading: a grey outline when the property isn't animated, amber with an amber label when it is, and filled amber when there is a keyframe at the playhead. Click it to add or remove the keyframe at the playhead. New keyframes ease Linear (change it in the Keyframe panel).
Align bar at the top: align left / centre / right / top / middle / bottom, distribute horizontally / vertically. One layer aligns to the canvas, several align to each other; distributing needs three or more. Below it, the boolean operations (union, subtract, intersect, exclude) for two or more shapes or paths (with fewer selected they grey out, and their tooltip says what they need), then, after a hairline, flip horizontal / vertical and rotate 90° right / left (as in the right-click menu).
Typing text: double-click a text on the canvas (or click it with the Text tool) to type in place. The real text stays visible with all its styles while you type; the panel's text box updates the canvas live too. While typing, the box has a handle on each side: drag one to set the width (Keep width turns on and lines wrap). When the text is only selected, its handles scale it like any layer. Space types a space while you edit. Esc, Ctrl+Enter or clicking away finishes. With the Text tool, click for text that grows as you type, or drag to draw a box of fixed width whose lines wrap.
Right-click menu (on the canvas or in the Layers panel):
Shortcuts: Ctrl X / C / V, Ctrl ⇧ V (paste in place), Ctrl ⇧ O (center origin), Ctrl ⇧ P (convert to path), Ctrl ⇧ E (export selection), Ctrl L (lock), Ctrl ⇧ ] / [ (bring to front / send to back). Ctrl C / V copy keyframes when keyframes are selected, and layers otherwise.
Editing several layers: on the canvas, one transform box around the whole selection (each layer's own box dashed inside it): its side and corner handles scale every layer from the opposite side (Shift keeps proportions, Alt scales from the centre), and its rotate handle turns them all around the box's centre (Shift: 15° steps). Dragging any of the selected layers, or anywhere inside the box (even over another layer), moves them all together, snapping the whole box (Shift: one direction, Ctrl: no snapping, Alt: drag copies); a click without dragging on another layer inside the box selects that layer. Each is one undo step; layers that are animated get a keyframe at the playhead. In the Inspector, the selection's section starts with the box around the selection: Position (X / Y) moves every layer with it, and Size (W / H, keeping proportions unless the link is off) scales them all from the box's top-left corner (their origins move and their Scale grows); layers that are animated get a keyframe at the playhead. Below it, with more than one layer selected, the inspector shows only the properties all of them have (transform, opacity, fill & stroke and stroke style when they are all shapes, compositing). A value they share is shown; one that differs reads Mixed, and a colour swatch shows •••. Typing a value, dragging a field's label, picking a colour, a toggle or the ◆ applies to every selected layer, as one undo step; for X / Y pairs each layer keeps its other value.
Animation presets (Animate ▸ Presets… in the timeline header or the main menu): ready-made animations for the selected layers, starting at the playhead. Groups: Entrances (fade in, slide up / down / left / right, pop in, zoom in, drop in, rotate in, flip in, elastic in, spiral in, skew in, unfold), Exits (the same, out; fold for unfold), Emphasis (pulse, pop, wiggle, shake, spin, swing, blink, heartbeat, bounce, rubber band, jello, tada, float, flicker, head shake, breathe, wobble, squash, glitch), Strokes (draw on, erase, travel, draw from middle, stroke pulse, undraw: shapes and paths), Colour (flash, dim, stroke flash, colour pulse) and Filters (blur in, blur out, glow pulse, shadow lift, soft focus; they add the blur or shadow they need, and play in JS exports only: a CSS export keeps filters still, so they carry a JS badge). Only the presets that fit the selection are listed. Each tile plays its preset on a small shape and names the tracks it animates (e.g. Position · Opacity). A preset writes keyframes only for the properties it animates and only inside its span, replacing any keyframes of those properties already there: entrances end on the value the layer already has, exits start from it, emphasis starts and ends on it, so presets slot into an existing animation. Hovering a tile previews it on the timeline (its span shaded on each layer, the keyframes it would replace outlined in red); the line beside Apply says the same in words (how many keyframes it replaces, in red). Length (each preset has its own default), Stagger (several layers start one after another) and Strength (how far it moves, grows or turns) adjust it. Double-click a tile, or Apply, to use it: one undo step, and the toast says how many keyframes it replaced, with an Undo button. A preset that runs past the end lengthens the animation.
Graph editor (the timeline bar's Graph button): one track opens into a value graph across time, under the playhead and keyframes like the rest of the timeline. It shows the selected keyframe's track, else the selected layer's first number track; click another property's name to graph that one. Number properties only (opacity, rotation, stroke width, trim, filter amounts…, and points such as position, scale or size, drawn as X and Y curves in two colours); colours, gradients, dash patterns and morphs have no graph, and their names say so. Drag a key to change its value and time (Shift keeps the drag to one direction), with the time and value shown as you drag. The selected segment (after the selected keyframe, or under the playhead) shows its easing handles: dragging one makes a custom cubic-bezier, shared by X and Y. Beside the graph, the property's values at the playhead (they follow it); click a key, then the Easing button, for the easing presets. One undo step per change.
Onion skin (the timeline bar's Onion skin button, View ▸ Onion skin, Alt+O): outlines of the selected layers a few steps before (blue) and after (red) the playhead, fading with distance. The arrow beside the button sets how many frames before and after, the time between them and the strength of the nearest ones (remembered in this browser). The ghosts are drawn only on the canvas, never exported, and hidden while playing.
Symbols: reusable artwork whose instances share their contents. Element ▸ Make symbol turns the selected group (or a new group of the selected layers) into a symbol; Duplicate (Ctrl+D) or Alt+drag an instance for more, or click the symbol in Assets ▸ Symbols (a card per symbol with a live thumbnail and its instance count; double-click the name to rename it, ✎ to edit it, × to delete it, which asks first when it has instances and leaves them as plain groups). Instances are marked in the Layers panel (the symbol icon and ×N) and in the inspector (Symbol · N instances). Each instance has its own transform, opacity and keyframes; shapes, styles and the contents' animation are shared. Editing a symbol: double-click an instance (or Element ▸ Edit symbol, or Edit symbol in the inspector): the rest of the scene dims and can't be picked, the other instances show as dashed outlines, and a chip on the canvas names the symbol with Done (Esc, or a double-click on an empty spot). A change there reaches every instance (one undo step). The inspector's Instance section has a Time offset (this instance's contents start later), Edit symbol and Detach instance (it becomes a plain group). The JS export writes each instance's contents once and the other instances with the same time offset as <use> of them, which the player animates for all; the CSS export, and offset instances, are written out in full.
Masks: select two or more layers, then choose Create mask. The top layer goes into a Masks folder inside a new Mask group, and the other layers show only where the mask is.
Boolean operations (second row of the align bar, or right-click ▸ Boolean): select two or more shapes or paths.
They're combined as they look at the playhead into one new path, which takes the bottom layer's place and style. Undo restores the originals.
Posing a rig: with Key whole rig on (the default; it sits in the Bone section of the rig's top bone), dragging any bone (FK, IK or Scale bone) keys every bone of the rig at the playhead, so each pose is stored whole. The new keys hold the values the bones already had there, and the easing they split is split with them, so nothing else in the animation changes. Turn it off for overlapping action (only the bone you drag is keyed). Turning it on for a rig whose poses have keys on some bones only offers to Add keys: every bone gets a key at each of the rig's key times, again without changing the motion; turning it on and adding the keys is one undo step. In the timeline, the top bone has a Pose row with a diamond at every time any bone of the rig has a key: click one to select all of them, drag to retime the pose, Delete to remove it.
Bending with pins: each pin pulls the path's nodes (and their handles) with it, nearer nodes more than far ones; a pin can also turn, bending the path around it. A pin that doesn't move holds the path in place. The Pins section of the inspector has a button per pin; the rows below it edit the selected pin: Position and Rotation, each with its own ◆, and Reach, how far every pin of the path pulls (100% by default). Remove pin and Remove all pins take them off (the path keeps its rest shape). In the timeline each pin has its own rows (Pin 2, Pin 2 · Rotation). Both exports bend the path: the JS player computes it, and the CSS export animates the path's shape with SMIL, as for morphs.
A rig's own draw order: nesting decides stacking, so a part can't normally sit behind its parent's artwork (a far arm behind the torso while it hangs on the torso's bone). Turn on Own draw order in the Bone section of the rig's top bone: a list of the artwork attached to the rig's bones appears, front at the top, starting in the order nesting draws it. Reorder it with the arrows or by dragging a row; the canvas and both exports draw the artwork in that order, each piece still moving with its bone (the Edit bone tool's rest pose too). Turning it off goes back to nesting. Artwork attached to the rig later goes in front. The order is not animated.
Pins that follow bones (Pin tool › Bind to bones) sit at a bone's joint and move and turn with the bone, so a rig bends a sleeve, a tail or a vine without keyframing the pins: the Pins section names the bone instead of Position and Rotation. Both exports follow the rig: the JS player works out where the bone is each frame, and the CSS export bakes the result like any other bend.
Pins on images, text and groups bend them through a triangle mesh, which the Pin tool draws over the artwork. The mesh is an even lattice of equilateral triangles laid over the content (and everywhere content inside a group moves during the animation, plus room for strokes and filters); triangles with nothing in them are left out, and the mesh is rebuilt by itself when the content changes. Each triangle shows the artwork clipped to it and moves it by the affine map its three corners give, so gradients, filters and text bend too and text stays editable. Mesh in the Pins section sets the density (it shows how many triangles the mesh has): a denser mesh bends more smoothly and costs more. The JS export computes the triangles every frame; the CSS export moves each one with SMIL, which stores every frame, so the Export dialog offers Use JavaScript when that is much smaller (it does the same for morphs). With the hover trigger, CSS mode animates the triangles with CSS, where faint seams can show between them in Chrome, and content inside a pinned group keeps playing when the pointer leaves. A pinned group's layers can't be picked on the canvas (use the Layers panel).
Snapping, grid, rulers and guides (the magnet and grid buttons in the canvas bar):
The guides are saved with the project; the other view settings are remembered in this browser.
Hyperlink: gives a layer a link (web address, mailto:, tel: or #anchor) that opens in a new tab or the same one. Clicking the layer in the exported SVG follows it.
Timeline follows the selection: selecting a layer (on the canvas, in the Layers panel or anywhere else) scrolls the timeline so its row and its property rows are in view; a layer already in view leaves the timeline where it is.
Timeline height: drag the timeline's top edge up or down. At its smallest it shows just the controls and the time ruler; at its largest it leaves a strip of the work area. The height is remembered in this browser; double-click the edge for the default height. With the edge focused, ↑/↓ resize it by 16px (Shift: 64px), Page Up / Page Down go to the largest / smallest, Home to the default. Resizing keeps the canvas zoom percentage and leaves the artwork where it is on screen; only in the fit view (Fit, 1) is the artboard fitted to the new size.
Timeline header: play controls, the time and the length field, then Easing and Animate, and on the right Graph, Onion skin and the zoom. Easing (the curve-with-handles button) is greyed out until keyframes are selected; clicking it opens the Easing panel on the left side of the window, just above the timeline (it covers little of the canvas): the keyframe's time, In / Out / In-Out, the easing presets 3 across (Linear, Ease, Hold, Basic, Sine, Quad, Cubic, Quart, Quint, Expo, Circ, Back, Elastic, Bounce, and your own), the curve editor (drag its two handles for a custom curve; its range fits overshooting curves such as Back and Elastic, and handles dragged past 0 or 1), Use on whole animator and Delete key; under the presets, Save as preset for a custom curve (or Remove preset for one of this project's). It follows the keyframe selection while open and closes with the button, Esc or a click elsewhere. Snap keyframes (dragged keyframes snap to the playhead, other keyframes and 0.05 s steps; Shift inverts) is in the canvas's snapping menu, on by default and separate from Enable snapping.
Timeline length: the timeline always fills its panel, past the end of the animation (that part is shaded). The white line marks the end: drag it to change the length (it snaps to 0.05s; Shift for 0.01s) and a tag shows the length while you drag. The length field (the total after the time in the timeline header, / 0:03.00) follows along; click it to type a length, in seconds (4, 2.5) or as a time (0:02.50): Enter applies, Esc puts it back, ↑ / ↓ and dragging work too. With the end line focused, ←/→ change the length by 0.1s (Shift: 1s).
Selecting in the timeline: click a layer's name to select it; Ctrl/Cmd+click adds or removes a layer, Shift+click selects the run of layers up to it. Clicking a track's title (Position, Fill…) selects all its keyframes (with Ctrl or Shift, adds them to the ones selected). With several layers selected, a track's ◆ adds (or removes) the keyframe at the playhead on every selected layer that has the property. Picking a track, a keyframe or a new track from Animate scrolls the Inspector to that property's row (opening its section) and highlights it.
Freezing a track: hover a property row in the timeline and click the snowflake, left of the property name. The track keeps its keyframes but stops using them: the property holds the value it had at the playhead, the row and its keyframes turn grey, no keyframes can be added, and exports leave it out. Editing the property meanwhile changes the held value. Click the snowflake again to unfreeze and go back to the keyframes.
Moving keyframes: with two or more position keyframes selected in the timeline (or one at the playhead), dragging the layer or using the arrow keys on the canvas moves all of them together (select them all to move the whole motion path). A single key selected at another time, such as the one Animate makes and selects, doesn't count: moving the layer keys its position at the playhead. Arrow keys pressed while working in the timeline still move the selected keyframes in time.
Web fonts in exports: Google Fonts are converted to outlines by default (no font download, identical everywhere, also inside <img>). Embed font instead keeps live, selectable text and embeds only the alphabets the text uses, which is smaller for long text. Hebrew is drawn in its right-to-left order; Arabic-script text is always embedded so its letters join. Fonts from this computer are converted or embedded the same way once the browser has shared them (embedding includes the whole font file); otherwise the SVG only names the font.
Click Agent in the top bar (Alt A) to open a chat docked on the right. Describe what you want, for example “draw a cartoon rocket and make it launch”, and the agent draws, edits and animates in this project with the editor's own tools: shapes and SVG artwork, layer edits, groups, presets, keyframes, shape morphing (Morph keyframes written as SVG path data; a rectangle, ellipse or star becomes a plain path) and the project settings. Its steps are listed as it works, and each request is one undo step: Undo, or Undo this request under its answer, rolls the whole request back.
Use the AI assistant you already pay for, such as Claude in Chrome or any assistant that can run scripts on web pages, to edit the open project with its own subscription. You chat with it in its own window; the Agent panel shows what it does.
svgcinema skill folder to add in Claude's settings) and just ask. The same svgcinema-skill.zip is in the site's downloads/ folder (written by npm run build from src/agent/skill.ts), for the website's Downloads page.window.svgcinema is the editing API for assistants: help() (a plain-text guide: how to work, the rules and every tool with its arguments, following your current settings), tools(name?), start({ task, assistant }), await call(tool, args), await batch([{ name, args }, …]) and done(summary). The tools are the design agent's own (shapes, SVG artwork, layers, presets, keyframes, shape morphing, project settings); a refusal comes back as { error } with the reason. Guide for AI assistants (in Settings, and on the connect screen) opens the same guide, for assistants that read it from a page.start() to done() (or Stop, or 90 seconds without a call) is one undo step. Earlier sessions fold to one line. Deleting layers asks you in the panel (Settings › Ask first).add_image); if it can't hand it over, the panel shows a drop card: save the image from the assistant's chat and drop it in, paste it (Ctrl V) or choose the file, or Draw vector instead; if it can't make pictures it draws vector shapes. Your image key: the image model of the API key settings, with your credit, asking first with the price; it can only be picked once an image model is set up there (until then it is greyed out, with Set one up in API key ›; once set up it shows the service, model and price, with Change ›). Vector shapes only: no pictures.Open… (Ctrl O) also opens .svg files: the SVG becomes a new project with its canvas (viewBox), its title as the project name, and its artwork as editable layers. It isn't saved yet (Save makes a .SVGcinema file). Import files…, dropping an SVG on the canvas, pasting SVG markup and placing an SVG from Assets read the file the same way into the current project, inside a group named after the file (the project's canvas and settings stay).
data-name, meaningful ids; other layers are named by kind, e.g. Rectangle 1).<style> classes) and inherited from groups are resolved. Linear and radial gradients (user-space or box units, transformed or not) become editable gradients.Open recent lists the last 8 projects you opened or saved, newest first, with when. In Chrome and Edge an entry opens the file itself (the browser may ask for permission again; a file that was moved or deleted drops off the list). Elsewhere, where the browser can't reopen files, it keeps a copy of the project as it was when last opened or saved (the newest 4 copies), and Save then writes a new file. The list stays in this browser; Clear the list empties it.
File names in any language: the project name (Hebrew, Russian, Japanese…) is used as typed for the .SVGcinema file and the exported .svg; only the characters file systems refuse (\ / : * ? " < > |) are left out. Code identifiers (component names in the embed code) still use a plain Latin form.
The Export window: the options sit in sections that collapse like the inspector's: Format (JavaScript or CSS only, the player, and a folded-away note on what each supports), Playback (trigger, what happens when it ends, repeat, direction, after the end, speed and frame rate, less motion), Size (responsive or fixed, dimensions, scale, largest width), Images and Text (only when the canvas has images or web / local fonts) and Advanced (element IDs, ID prefix, the editable project), which starts closed. A closed section shows a one-line summary of its settings, and the sections you close stay closed (remembered in this browser). Only the options column scrolls: the preview, the file size and the Copy / Download buttons stay in view.
Export › Image: the picker at the top of the Export window (Animated SVG · Image · Video · GIF & Animated WebP) switches what it makes. Image saves one frame of the animation as a picture: the frame under the playhead. The playhead under the preview is the editor's own: drag it, type a time, or step one frame (1/30 s) with the arrows, and the editor's playhead moves with it.
<img> for PNG, JPEG and SVG; for WebP, AVIF and JPEG XL a <picture> that lists the newer formats first with PNG as the fallback. 2× versions for high-density screens and the fallbacks are ticked by default; Download the set (.zip) makes every file the code names.Export › Video makes the animation into a video file, every frame drawn exactly and encoded by the browser's own encoder (WebCodecs), faster than real time; the files are written by SVGcinema itself (no video library).
<video> (autoplay muted loop playsinline) with WebM and MP4 sources and a poster image; Export the set (.zip) makes them all.Export › GIF & Animated WebP makes an animated image for places that don't play SVG or video: email, chat apps, docs. Both encoders run in a worker on frames drawn exactly; the GIF encoder is SVGcinema's own (no library), the WebP frames come from libwebp.
<img>; an Animated WebP in a <picture> with a GIF behind it for email and older apps. Download the set (.zip) makes both.Linked to scroll (Playback › Starts, JavaScript export): the animation follows the page's scroll instead of playing on its own. Scrolling down plays it and scrolling back plays it backwards; Repeat, Direction, After the end and Speed don't apply and are hidden.
<object> with a file from the same site (the player measures the <object> in the page around it). Inside a box that scrolls on its own, it follows that box. An <img> shows the first frame only, and CSS-only exports can't link to scroll (they keep On load).Less motion: the Export dialog's Less motion setting decides what visitors who ask their system for reduced motion see: Play normally (the default, adds nothing), Show the first frame or Show the last frame. The JS player then holds that frame and doesn't start (whatever the trigger); a CSS export pauses every animation on it (a few bytes of CSS). Shape morphs in a CSS export are held with the CSS d property, which Safari ignores, so there they keep playing.
SVGs exported by SVGcinema: tick Include the editable project in the Export dialog's Advanced section (off by default; it shows how much it adds, about a third more for a typical JS export before compression). The project goes into the SVG's metadata, the pictures stay the SVG's own, and opening the SVG in SVGcinema brings back the exact project (layers, keyframes, settings); exporting it again gives the same file. Added to another project, its layers come in a group. Without it, an exported SVG opens as its artwork (the first frame), not animated.
Animated SVGs from SVGator keep their animation as ordinary, editable keyframes (opened as a project, they also bring their length, repeat, direction, fill mode, speed, frame rate and start trigger; added to a project, they lengthen its timeline when they are longer).
offset-path with its curves, and the CSS keyframes (with their per-keyframe easing) become the same keyframes the JS export gives. SVGator's CSS writes the anchor point's offset twice, which shifts animated elements in browsers (in its own preview too); the import reads it once, so the result matches the JS export. Its CSS blur amounts are converted back as well. (SVGator can't export shape morphs as CSS.)@keyframes on elements) also become keyframes: opacity, fill and stroke, their opacities, stroke width and dashes, sizes, and transforms written as translate · rotate · skew · scale around a transform-origin.The Export dialog's Player row (Format section, JavaScript exports only) chooses Embedded, the default (the player inside each SVG: plays anywhere, even offline, about 26 KB), or External. An amber note under it reminds that a script export doesn't play as <img> or as a CSS background (export as CSS only for those); the footer says player inside or external player beside the file size.
SVGs exported with External carry only their artwork and animation, as JSON in <script type="application/json" data-cin>, plus a loader of under 1 KB. Once the page has loaded, the loader uses the page's own player (window.CinPlayer) if it has one; otherwise the first SVG on the page loads it from jsDelivr, https://cdn.jsdelivr.net/npm/svgcinema-player@<version>/cin-player.min.js (the version is fixed in the address, so later player releases never change files already exported). Either way every animation on the page shares one player. A standalone .svg (or one in <object>) loads its own copy from jsDelivr.
Self-hosting: the Embed tab's HTML code (with External) starts with an optional <script src="YOUR-ADDRESS/cin-player.min.js"> line before the SVG pasted inline, and Download the player (version) next to Copy snippet gets that file from the site's downloads/ folder (cin-player.min.js and cin-player-<version>.min.js, with an index page; also on npm).
The player is src/runtime/player.ts; src/runtime/boot.ts reads the JSON and wires the start trigger as the embedded player does. The JSON carries a data format number (f); a player only starts SVGs whose format it knows. npm run build:player (also part of npm run build) makes player/cin-player.min.js, about 26 KB minified.
API: each SVG's player is svg.player (also svg.cinPlayer), and the SVG fires cinready when it's ready. window.CinPlayer has version, f (the data format), boot(svg) (for SVGs added later by script) and scan(root).
Publishing: the package is player/ (svgcinema-player). .github/workflows/player.yml publishes it when player/package.json has a version npm doesn't have yet; it needs the repository secret NPM_TOKEN. To release player changes, raise that version: exports then link to the new one. npm holds a release published from GitHub until the owner approves it (npmjs.com ▸ Staged Packages, with 2FA). The README on npm changes only with a new release.
npm run build:converter makes tools/converter/dist/SVGcinema-Converter.html: one HTML file with the converter and the whole editor inside it. It isn't deployed or linked anywhere. Open it from disk (Chrome or Edge to save into a folder) and it turns many SVGs into .SVGcinema projects at once: SVGator exports (JS or CSS), CSS-animated SVGs and plain artwork.
.zip.The editor converts in a hidden frame inside the file (src/io/batch.ts). Each SVG goes through the same Open and Save as by hand, and nothing is kept in the browser (src/core/batch-mode.ts). Sources: tools/converter/ (page and build) and src/convert/ (its script and styles). Rebuild it after editor changes to pick them up.
.SVGcinema project formatA .SVGcinema file is a standard zip archive:
| Entry | Contents |
|---|---|
thumbnail.png | The first frame, 512 px on its long side: the first entry, stored uncompressed, for file managers' thumbnail handlers |
manifest.json | format: "svgcinema-project", version, name, save time, canvas size, counts |
project.json | The document: layers, keyframes, easing presets, export settings |
assets/index.json | One entry per asset: id, name, kind, MIME type, size in pixels and bytes, file path |
assets/<id>-<name>.<ext> | Each image as its original file |
agent-chat.json | The design agent's chat for this project (text only), when there is one |
project.json references assets by id, so images are stored once.
Thumbnails in your file manager: optional add-ons read thumbnail.png so projects show their first frame instead of a plain icon:
SVGcinema-Thumbnails-Setup.zip, holding SVGcinema-Thumbnails-Setup.exe (zipped because many web hosts refuse to serve .exe files). It installs for you only, with no administrator rights. It's unsigned, so Windows may warn about an unknown publisher: choose More info, then Run anyway.svgcinema-thumbnails.deb, or the .tar.gz with install.sh. Works in GNOME Files, Nemo, Caja and Thunar.The editor links to them from Help › Thumbnails on your computer… (hidden on a Mac, which isn't supported). It offers the download for the system you're on first, with the Windows warning highlighted. The website has a section with both. The build puts the downloads in the site's downloads/ folder next to the editor (editor/downloads/ in the deploy bundle). The Pages workflow fetches the installers from the thumbnails release first; for a local build, run npm run fetch:downloads (needs gh). The sources and how they work are in thumbnailers/.
The document model lives in src/model/types.ts: Doc, Layer, Geometry, BaseValues, Tracks, ExportSettings, Asset, plus the transient EditorState (S) and Drag. The runtime's EngineLayer is the exported subset of Layer.
tsconfig.json is strict, including noImplicitAny, with two exceptions:
strictNullChecks is off;useUnknownInCatchVariables is off.Explicit any remains where the code works with the page directly: $() and byId() lookups, a few panel elements in core/dom.ts, and e.target in delegated event handlers. Typing those is the next step, before strictNullChecks is switched on.
Conventions:
S.drag. Its move(p, e) and up(p, e) get their types from Drag, so don't annotate them.ICON, EXT_OF, ...) are typed Record<string, ...>.ReturnType<typeof setTimeout>, so the code compiles with or without Node types.tests/engine.test.ts: easing, interpolation, the player API, and that the player can be embedded.tests/zip.test.ts: the .SVGcinema container.tests/geometry.test.ts: path parsing and morph helpers.tests/output.test.ts: compares the exported SVG (JS and CSS modes, every trigger) and every embed snippet against tests/fixtures/export-sample.json. It also checks that no editor TypeScript syntax leaks into a snippet. After an intended change to the output, regenerate the reference with UPDATE_FIXTURES=1 npx vitest run tests/output.test.ts and review the diff..github/workflows/pages.yml tests, builds and publishes dist/ on every push to master (or on demand from the Actions tab).
One-time setup: Settings → Pages → Build and deployment → Source: GitHub Actions. Pages on a private repository needs a paid GitHub plan; otherwise make the repository public.
The site is served at https://<user>.github.io/<repo>/. The Vite base: './' setting makes asset paths relative, so no extra configuration is needed.
Website: site/ holds the SVGcinema website that introduces the editor and links into it: a short home page (the live hero, the workflow, the design agent, highlights, export) and a page each for Features, Design agent, Import, Export, Downloads and the Licence, with captures of the real editor in light and dark. node site/build.mjs builds the pages (site/index.html, site/features/index.html, …), which expect the editor in an editor/ folder beside the home page; npm run build:deploy puts them together in deploy/ for any static host, and npm run build:zip makes the whole site (thumbnail installers, player and Claude skill included) as svgcinema-site.zip. The Pages workflow does not publish it yet. See site/README.md.
The code is ready for a Tauri shell without changes. When it gets wrapped:
io/save.ts and io/open.ts with Tauri's dialog and fs plugins. This gives true overwrite-on-save, recent files and .SVGcinema file associations.optimizeAsset interface in export/images.ts.SVGcinema targets current Chrome, Edge, Firefox and Safari.
<animate>, because Safari doesn't support the CSS d: path() property. With the hover trigger it stays d: path() (CSS pauses it on mouse out, which SMIL ignores), so in Safari those shapes don't move.SVGcinema is proprietary software: © 2026 asafco83, all rights reserved. You may use the app to make animations, and the files you export are yours to use for anything, including commercial work. The external player file (svgcinema-player) may be loaded from a CDN or hosted unmodified, only to play SVGcinema exports. The source code may not be copied, changed or redistributed without permission. The owner may change or revoke the terms, and change or end access, at any time. See LICENSE for the full terms.
| Scale bone | S | Drag along a bone to scale it, keyed at the playhead (the drag makes the Scale track). The options bar at the top of the work area picks what a drag changes: Length (W, along the bone), Thickness (H, across it, drag sideways), Linked (both), or Squash & stretch (W up, H down: Volume Area keeps W × H, Tube keeps W × H²). Tab while dragging switches the mode, Shift snaps to 10%. Child bones don't inherit a bone's scale unless their Inherit scale is on: they follow the stretched tip without being stretched, in the editor and in both exports |
| Bind | Click a bone, a layer or a pin (Shift+click adds more; pins show with Show pins), then click the bone it should hang on. A pin binds where it is (its offset from the bone's joint is kept) and from then on follows the bone; clicking empty canvas with pins picked unbinds them where they rest. Over empty canvas, when a click would unbind something, an ✕ beside the pointer says Unbind and what. It keeps its place on the canvas, and its keyframes keep their positions at their times. A bone dropped near the new parent's tip snaps onto it (Alt keeps the offset). Clicking empty canvas unbinds the picks (they move to the group that holds the rig). A bone can't go under itself or its children. Esc clears the picks |
| Pin group | Hold or right-click the button for the flyout |
| Pin tool | W | Click a path, shape, image, text or group to add a pin (rectangles and ellipses become paths). As with Select, a click picks the outermost layer and Ctrl+click picks inside a group; the selected layer keeps getting the pins. Drag a pin to bend the artwork, keyed at the playhead (the first drag makes the pin's track, starting from its rest place at 0 s). Drag the ring of the selected pin to turn it (Shift snaps to 15°). Alt+drag moves a pin at rest without bending. Delete removes the selected pin. While the artwork is bent, a dashed outline shows it at rest and a dashed dot where each moved pin sits at rest; a chip above the canvas names the layer and its pins (and mesh). Bind to bones (in the options bar above the canvas, when there are bones) shows the bones: click one to add a pin at its joint that follows the bone, moving and turning with it, so the rig bends the artwork; click a bound bone again to unbind it, Esc when done. Bound pins are drawn in red, the playhead's colour, with a ring around them (their buttons in the Pins section are red-edged); they can be picked and deleted but not dragged, and they work together with ordinary pins (a bone inside a pinned group can't take its pins: it already moves the artwork) |
| Edit pins | Moves pins at rest: the artwork shows its rest shape (a Rest pose chip says so), and a pin you drag takes its motion with it (its keys move by the same amount). Shift moves straight. Delete removes the selected pin |
| Hand | H | Drag to pan. Hold Space for the hand in any tool (a tap plays/pauses). Middle-drag pans, the wheel scrolls, Ctrl+wheel zooms at the pointer, double-click the hand to fit |
Text: content (spell-checked by the browser, here and when typing on the canvas, with a red wavy underline and the browser's suggestions on right-click; a double-click on the text underlines its misspelt words straight away; Help ▸ Spell check turns it off), position and measured size. Convert to shapes turns the letters into one path layer (same name, place, style and animation) when the font is a Google Font or a font on this computer; basic font stacks look different on every computer, so they can't. Hebrew is drawn in its right-to-left order; Arabic-script text, whose letters join, can't be converted yet.
Size (rectangles, ellipses, images, paths, including polygons, stars and lines, and imported elements; in the layer's own section): width and height as you see them (the shape's own size times its scale, so scaling updates them; a value typed in is divided back by the scale), live on the canvas while you drag the W / H labels, animatable with its own ◆. The shape grows and shrinks around its origin (put the origin on an edge or corner with the anchor grid to grow it from there). Unlike Scale, Size changes the shape itself, so the stroke width stays the same. An imported element (kept as imported) is stretched from its own box instead, like Scale, so its strokes and text stretch with it. The link toggle beside the fields keeps the proportions, as for Scale (on by default for images, off for shapes). Text and groups use Scale.
Shape settings (at the top for these shapes): polygon Sides; star Points and Ratio (inner / outer radius); corner Radius for rectangles, polygons and stars (capped so corners never overlap, and animatable: the runtime rebuilds the rounded path every frame); line From / To, each keyable on its own so a line can grow or swing. Polygons and stars keep their settings until their nodes are edited by hand, they get Morph keyframes, they are stretched unevenly or Convert to path (in their section) is used; then they are plain paths. A rectangle, polygon or star whose corner radius is animated keeps that animation when it becomes a plain path: its Radius keyframes become Morph keyframes (same times and easing).
Number fields: drag the label at the left of any number field (the X in X 162) left or right to change the value: one unit per pixel, Shift ×10, Alt ×0.1 (finer steps for small units such as seconds). A drag is one undo step; a plain click on the label puts the caret in the field. In a number field, ↑ / ↓ change the value live by 1 (Shift 10, Alt 0.1; 0.1 s for times); holding the key is one undo step. Toggles beside a pair of fields (keep proportions, rotate along the path, centre origin) have a column of their own, so every pair of fields keeps the same width.
Transforms: position, scale (with a keep-proportions link), skew X/Y, rotate as turns + degrees (with a rotate along the motion path toggle when the layer has one), origin, anchor grid, flip.
Motion path: Set motion path makes the layer follow a path, rectangle or ellipse over the whole animation (one keyframe per point, timed for an even speed). Even speed along curves moves along each curved segment at a constant speed (by its length) instead of following the curve's own spacing. On the canvas, drag a segment of the path to curve it, drag its handles to fine-tune, double-click a segment to straighten it. Path node (below it) edits the keyframe at the playhead, or the one before it: its type (straight, mirrored, asymmetric, disconnected) and position; ‹ › step between nodes. Alt-drag a handle to move it alone.
Text appearance: font: the field opens a searchable list, one line per font: its name on the left and, on the right, the layer's own text in that font (or “Sample” in its script: דוגמה, Пример, ★ ✦ ☀… when the font lacks those letters). Chips filter by type (serif, sans, display, script, mono) and a menu by script. The list holds Favourites (★, remembered in this browser), Recent (the last five fonts chosen), the basic system stacks, 36 Google Fonts grouped by script (Hebrew, Latin, Cyrillic, Greek, Arabic, Symbols; a font covering several scripts is listed under each, and the group headings stay pinned while you scroll), and fonts on this computer (Chrome/Edge can list them after you allow it; elsewhere type a font name). Preview faces load as their rows scroll into view. Arrow keys and Enter choose, Esc closes. Then style (weight × italic), bold / italic / underline / overline / strikethrough, size, letter spacing, alignment, direction (left to right / right to left: automatic from the first letter until you pick one; the Align buttons stay as you see them, and right-to-left text starts at the right, so it grows leftwards as you type), case (as typed, UPPERCASE, lowercase, Capitalise Each Word: the typed text is kept, only its display changes), line height, and Keep width: when on, the box keeps its width and lines break to fit it.
Fill & stroke: each colour row has a swatch, an eyedropper and the value as text (a colour, none, or Linear / Radial for a gradient). Click the swatch to open the colour window. Its top row picks the type: None, Solid, Linear or Radial.
Appearance: Trim path (Start / End / Offset %, all animatable) shows part of a stroke, for draw-on and travelling-dash effects. Also: paint order, blending, isolate, opacity slider, line ends, line join, miter limit, stroke alignment (Align: centred, inside or outside the edge, on closed shapes: rectangles, ellipses, text and closed paths; SVG has no stroke alignment, so it is drawn at twice the width and clipped to the shape, or masked to outside it, and plays the same in JS and CSS exports, an animated shape included), dash pattern (animatable: patterns of different lengths blend by repeating to a common length, and no pattern blends as zeros), dash offset, path length. (To draw a line on, animate Trim End from 0% to 100%: Shift+D.)
Filters: every parameter animatable (in JS export mode; CSS mode exports filters as static). Add filter… opens a grouped menu:
Morph (paths, rectangles and ellipses; the section was called Path & morph): the node count, the Morph ◆, Edit nodes, Morph into another shape and Add nodes (subdivide). A rectangle or ellipse becomes a plain path when it starts morphing (Edit nodes, a Morph keyframe, Morph into or Add nodes); a rectangle's Radius keyframes become Morph keyframes, so it looks the same.
.SVGcinema file (text only) and kept in this browser between visits. To save a project without it, uncheck Include AI agent chats in the Save dialog (Save keeps that choice). New chat (the pencil button) starts over, with Undo in the notice.