SVG scenes
Use
template svg:to wire Interact controls / Computed / Timer into a declarative SVG scene — draw springs, pendulums, planets, circuits, any 2D physics / geometry animation. Freer than Vega-Lite chart (any shape) but requires SVG knowledge.
When to use
- ✅ Physics simulations (springs / pendulums / planetary orbits / damped oscillators)
- ✅ Geometry demonstrations (trigonometry / circular motion / reflection-refraction / geometric transforms)
- ✅ Circuit / fluid / optical-path diagrams (with dynamic parameters)
- ✅ Custom icons / data visualization (shapes vega can't express)
- ✅ Simple character animations (robot waving, expression changes, toy motion)
- ❌ Data charts (line / bar / scatter) → use Vega-Lite, much simpler
- ❌ Complex 3D (SVG is 2D; for 3D use WebGL / Three.js — Rho doesn't directly support)
- ❌ Pixel art / bitmap (use
<img>, not SVG)
Basic syntax
\```interact
slider angle 0 360 0 1
template svg:
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke="#888" stroke-width="0.5"/>
<line x1="50" y1="50"
x2="{50 + 40*cos(angle*pi/180):.2f}"
y2="{50 + 40*sin(angle*pi/180):.2f}"
stroke="red" stroke-width="2"/>
</svg>
\```
After template svg: is SVG markup — {var} placeholders work in any attribute.
Renders: a circle + a red line from center, drag the angle slider to rotate the line.
Placeholders in SVG attributes
{expression} can appear in any SVG attribute value, evaluated and substituted:
<circle cx="{50 + 40*cos(angle*pi/180):.2f}"
cy="{50 + 40*sin(angle*pi/180):.2f}"
r="{radius}"
fill="{isActive ? 'red' : 'blue'}"
opacity="{alpha:.2f}"/>
The mini DSL operations (see Computed) — trigonometric / power / ternary — all usable inside {}.
Safety (SVG sanitizer)
Rho includes a built-in SVG sanitizer that strips dangerous elements / attributes:
| Stripped | Why |
|---|---|
<script> |
Arbitrary JS execution |
<foreignObject> |
Embeds HTML (potential XSS) |
on* event attrs (onclick / onload / etc.) |
Inline JS |
href containing javascript: URLs |
XSS |
<image href="external://..."> |
Prevent external resource tracking |
Preserved:
- Standard SVG shapes (rect, circle, line, path, polygon, ellipse, polyline)
- Text (text, tspan)
- Containers (g, defs, symbol, use)
- Gradients / patterns (linearGradient, radialGradient, pattern)
- Standard attributes (cx, cy, x, y, width, height, fill, stroke, transform, etc.)
<title>/<desc>(accessibility-friendly)<animate>/<animateTransform>(standard SVG animation)
The safety design means SVG is fully declarative + cannot execute JS — extending Rho's overall safety stance.
Examples
Example 1: circular motion (manual sliders)
\```interact
slider angle 0 360 0 1
slider radius 10 50 40 1
template svg:
<svg viewBox="0 0 100 100" width="200" height="200">
<circle cx="50" cy="50" r="{radius}" fill="none" stroke="#888" stroke-width="0.5" stroke-dasharray="2,2"/>
<line x1="50" y1="50"
x2="{50 + radius*cos(angle*pi/180):.2f}"
y2="{50 + radius*sin(angle*pi/180):.2f}"
stroke="red" stroke-width="1.5"/>
<circle cx="{50 + radius*cos(angle*pi/180):.2f}"
cy="{50 + radius*sin(angle*pi/180):.2f}"
r="3" fill="red"/>
</svg>
\```
Renders: dashed circle + red line + red dot. Two sliders control radius and angle simultaneously.
Example 2: auto-rotation (timer + SVG)
\```interact
timer angle 0 360 2 loop
template svg:
<svg viewBox="0 0 100 100" width="150" height="150">
<g transform="translate(50, 50)">
<circle r="40" fill="none" stroke="#888"/>
<g transform="rotate({angle:.0f})">
<line x1="0" y1="0" x2="40" y2="0" stroke="red" stroke-width="2"/>
<circle cx="40" cy="0" r="3" fill="red"/>
</g>
</g>
</svg>
\```
Renders: auto-rotating red line + dot (2° every 50ms; full circle in ~9 seconds). Using SVG <g transform="rotate(...)"> is more intuitive than computing cos/sin directly.
Example 3: full spring oscillator simulation
\```interact
timer t 0 10 0.05 loop
slider amplitude 5 40 25 1
slider period 0.5 4 2 0.1
template svg:
<svg viewBox="0 0 200 100" width="400" height="200">
<line x1="20" y1="50" x2="180" y2="50" stroke="#ccc" stroke-dasharray="3,3"/>
<line x1="100" y1="20" x2="100" y2="80" stroke="#aaa" stroke-width="0.5"/>
<text x="100" y="15" text-anchor="middle" font-size="6" fill="#888">Equilibrium</text>
<circle cx="{100 + amplitude * sin(2*pi*t/period):.2f}" cy="50" r="6" fill="red"/>
</svg>
\```
Renders: horizontal line (ground) + equilibrium marker + red ball doing simple harmonic motion. Two sliders control amplitude and period; timer drives time.
Example 4: simplified solar system
\```interact
timer t 0 1000 1 loop
template svg:
<svg viewBox="0 0 200 200" width="400" height="400">
<!-- Sun -->
<circle cx="100" cy="100" r="8" fill="orange"/>
<!-- Earth orbit -->
<circle cx="100" cy="100" r="40" fill="none" stroke="#ccc" stroke-width="0.5" stroke-dasharray="2"/>
<circle cx="{100 + 40*cos(t*0.1):.2f}" cy="{100 + 40*sin(t*0.1):.2f}" r="4" fill="blue"/>
<!-- Mars orbit (longer period) -->
<circle cx="100" cy="100" r="60" fill="none" stroke="#ccc" stroke-width="0.5" stroke-dasharray="2"/>
<circle cx="{100 + 60*cos(t*0.05):.2f}" cy="{100 + 60*sin(t*0.05):.2f}" r="3" fill="red"/>
<!-- Jupiter orbit (longer period) -->
<circle cx="100" cy="100" r="80" fill="none" stroke="#ccc" stroke-width="0.5" stroke-dasharray="2"/>
<circle cx="{100 + 80*cos(t*0.02):.2f}" cy="{100 + 80*sin(t*0.02):.2f}" r="6" fill="brown"/>
</svg>
\```
Renders: Sun (orange) + Earth (blue) + Mars (red) + Jupiter (brown), each with different orbital periods (Earth fastest, Jupiter slowest). The timer drives all planets in sync.
Plain-text fallback behavior
\```interact
slider angle 0 360 0 1
template svg:
<svg viewBox="0 0 100 100">
...
</svg>
\```
→ Non-supporting readers show as a code block — readers see the full SVG markup (with {angle} placeholders); anyone with SVG knowledge can imagine the picture.
| Reader | Render |
|---|---|
| Rho | Full interactive SVG scene |
| GitHub web | Code block (SVG markup as literal) |
| Obsidian | Same |
| VS Code default preview | Same |
| cat / less | Plain text |
Note: GitHub and the like don't auto-render SVG markup (even if you write
<svg>directly in markdown). It takes Rho's svg template to bring it to life.
Common pitfalls
1. SVG syntax error (missing closing tag)
template svg:
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40"
← ❌ Missing />
</svg>
→ SVG fails to parse; the whole block doesn't render. SVG is strict XML — all self-closing tags need />, all elements need a paired close.
Recommendation: design in an SVG editor (Inkscape / SVG-Edit / Boxy SVG) and tweak the export.
2. Placeholder in wrong position
template svg:
<circle cx={x} cy={y}> ← ❌ Missing quotes
<circle cx="{x}" cy="{y}"> ← ✅
SVG attribute values must be in double quotes — even when substituting numbers.
3. mini DSL doesn't have that function
template svg:
<rect ... transform="rotate({Math.atan2(dy, dx)})"> ← ❌ Math.atan2 doesn't exist
← ✅ use atan2(dy, dx) (mini DSL builtin)
mini DSL isn't JS — Math.X / Number.X don't exist. Use Computed function list directly.
4. Trying to embed HTML / script
template svg:
<svg>
<foreignObject>...</foreignObject> ← ❌ Stripped by sanitizer
<script>...</script> ← ❌ Stripped
<a href="javascript:...">...</a> ← ❌ href cleared
</svg>
→ Rho's safety policy deliberately blocks these. If you need such capabilities, Rho isn't the right tool.
5. No viewBox → render anomaly
template svg:
<svg width="200" height="200"> ← ⚠️ Missing viewBox, scaling unpredictable
<svg viewBox="0 0 200 200" width="200" height="200"> ← ✅ Recommended
Always set viewBox — it defines the internal coordinate system; outer width/height controls actual display size; together they make it scalable.
6. Performance: too many elements
template svg:
<svg>
<!-- 1000 circles re-rendering every frame → browser hangs -->
<circle ... />
<circle ... />
... × 1000
</svg>
→ Element count directly impacts render performance. < 200 elements is most comfortable. For more, use vega-lite or canvas (Rho doesn't support the latter).
7. Jumpy animation (discrete expressions)
template svg:
<circle cx="{floor(t*10)}" cy="50" r="3"/> ← ⚠️ Using floor jumps 10x per second (looks janky)
<circle cx="{t*10:.2f}" cy="50" r="3"/> ← ✅ Smooth interpolation
Avoid floor / ceil / round in expressions (unless you actually want the discrete effect).
Recommended SVG learning resources
If you're not familiar with SVG:
- MDN SVG tutorial
- SVG-Edit online editor — design and export markup
- Boxy SVG — desktop SVG editor
Once comfortable, this page covers 90% of what you'll do for Rho animations.
See also
- Interact controls — sliders etc. driving SVG
- Timer — auto-playing SVG animation
- Computed — full mini DSL function list
- Interact + Vega-Lite chart — standard charts (not SVG)
- Shared state (namespace)
- Plain-text fallback principle