Typing & tap overlays
Add typed strings and click ripples on top of captured frames.
Overlays let you show actions that aren't easy to capture as plain HTML states — a user typing into a search box, a finger tap on a button. They sit on top of the captured frame for the duration of that frame.
The two overlay kinds are typing (a string revealed character
by character) and tap (a Material-style ripple). Both are added
via the overlays array on an AnimationFrame.
Recipe: terminal typing demo
This builds a two-frame animation showing the user type a command, then its output appearing.
const WIDTH = 720, HEIGHT = 240;
// Frame 1 is an empty terminal prompt; the typing overlay drives the action.
const emptyPrompt = `<!doctype html>
<html><body style="margin:0;background:#1e1e2e;font-family:'SF Mono',monospace;color:#e6edf3">
<div style="padding:18px;font-size:13px;">
<span style="color:#28c840;font-weight:700;">$</span>
</div>
</body></html>`;
// Frame 2 is the prompt with the command + output already painted.
const withOutput = `<!doctype html>
<html><body style="margin:0;background:#1e1e2e;font-family:'SF Mono',monospace;color:#e6edf3">
<div style="padding:18px;font-size:13px;line-height:1.7;">
<div><span style="color:#28c840;font-weight:700;">$</span> npm install domotion-svg</div>
<div style="color:#56d364;">+ domotion-svg@0.1.0</div>
<div style="color:#8b8fa3;">added 12 packages in 1.4s</div>
</div>
</body></html>`;
// Capture both as usual, then attach a typing overlay to frame 1.
frames.push({
svgContent: elementTreeToSvg(tree0, WIDTH, HEIGHT, "f0-"),
duration: 2200,
overlays: [{
kind: "typing",
text: "npm install domotion-svg",
x: 36, y: 35, // just to the right of the $ prompt
fontSize: 13,
color: "#e6edf3",
speed: 45, // ms per character
delay: 300, // ms before typing starts
}],
transition: { type: "crossfade", duration: 350 },
});
frames.push({
svgContent: elementTreeToSvg(tree1, WIDTH, HEIGHT, "f1-"),
duration: 3500,
});
Tuning the typing
- x, y are the SVG coordinates where the text starts. Look at the captured frame's SVG to find them — typing happens in the same coordinate space as the captured content.
- speed defaults to 60 ms/char. 30–45 feels brisk (good for short commands); 70–90 feels deliberate (good for showing a longer search query).
- delay defaults to 300 ms. Bump it if the captured frame should "rest" before the typing starts.
- bgColor lets you mask placeholder text in the
captured input — set it to the input's background color and pad
bgWidth/bgHeightto cover the placeholder.
Recipe: tap ripple on mobile UI
Tap overlays are a Material-style ripple — useful when the captured frame shows a "before" state and you want to communicate "user tapped here" before crossfading to the "after" state.
frames.push({
svgContent: elementTreeToSvg(beforeTree, 390, 844, "a-"),
duration: 1800,
overlays: [{
kind: "tap",
x: 320, y: 760, // center of the FAB
delay: 900, // fire ~halfway through the frame
}],
transition: { type: "crossfade", duration: 280 },
});
frames.push({
svgContent: elementTreeToSvg(afterTree, 390, 844, "b-"),
duration: 2200,
});
The ripple is white-translucent and works against most backgrounds.
For light backgrounds, a slightly larger frame's delay
(~300 ms before the crossfade starts) makes the tap feel more
intentional.
Stacking overlays
You can put multiple overlays on a single frame — typing in the search box, then a tap on the suggestion that appears:
overlays: [
{ kind: "typing", text: "design system", x: 100, y: 80, speed: 50 },
{ kind: "tap", x: 140, y: 160, delay: 1500 },
]
When to capture state into HTML instead
Overlays are convenient but they have two costs:
- They disable the merge fast path. Even one overlay forces every frame to render atomically, which inflates output size and may produce one-frame flicker between crossfaded frames.
- They live in their own coordinate space. If the captured layout shifts (different viewport size, font metric change), the overlay coordinates don't move with it.
Where the action you want to show could be expressed as captured HTML — "input filled in", "button pressed" — prefer that.