Going 3D
So far every shape has lived on a flat page. manic has a second world: a real 3D space you look at through a camera. The idea is exactly the same — you name a cast of shapes and call them out in a script — but now the shapes have depth, and you can spin the camera around them.
One rule of thumb: everything 3D ends in 3 — cube3, sphere3, move3,
orbit3. That’s how you tell the two worlds apart.
First, a camera
A 3D scene needs a camera — an eye to look through. You say where the eye sits and which point it looks at:
camera3((8, -10, 6), (0, 0, 1), 45); // eye position, look-at point, zoom
Positions are (x, y, z), and here z is up (x and y are the ground). You
add one camera, and you can swing it around later.
▶ See it play:
The 3D cast
| shape | write | draws |
|---|---|---|
| cube3 | cube3(box, (0,0,1), (2,2,2)); | a box (width, depth, height) |
| sphere3 | sphere3(ball, (0,0,1), 0.9); | a ball of that radius |
| point3 | point3(p, (1,1,1)); | a small marker in space |
| line3 / arrow3 | arrow3(v, (0,0,0), (0,0,2)); | a segment / a vector |
| grid3 | grid3(floor, (0,0,0), 5, 1); | a ground grid to sit things on |
| axes3 | axes3(ax, (0,0,0), 3); | labelled x, y, z arrows |
Style and reveal them with words you already know — color, opacity, show,
flash:
cube3(box, (0, 0, 1), (2, 2, 2)); color(box, cyan);
show(box, 0.6);
Labelling a point in space
To put words on a 3D point, make an ordinary 2D text and pin it there with
pin3. As the camera moves, the label sticks to its point:
text(tag, (0, 0), "origin");
pin3(tag, (0, 0, 0));
Curves and surfaces
Draw a wire through space from three formulas of t (a helix, here), or a
surface from a height formula z = f(x, y):
curve3(helix, "cos(t)", "sin(t)", "t*0.2", (0, 12));
surface3(wave, "sin(x)*cos(y)", (-3, 3), (-3, 3));
For shapes a plain height field can’t make — a torus, a Möbius strip —
use param3, which takes three formulas of two parameters, u and v:
param3(torus, "(3 + cos(v))*cos(u)", "(3 + cos(v))*sin(u)", "sin(v)",
(0, 6.28), (0, 6.28));
One formula rule: always put
*between names. Writepi*t, neverpit(manic readspitas one unknown word). Same forv*v, notvv.
Solids
Build filled, shaded solids:
prism3/pyramid3— n-sided prisms and cones (use many sides for a cylinder or a smooth cone).revolve3— spin a radius profiler(t)around the upright axis (vases, spheres, lathe shapes).extrude3— lift a flat 2D shape (even a boolean cut-out) straight up into a solid.
prism3(hex, (0, 0, 1), 6, 1, 2);
revolve3(vase, (3, 0, 1.5), "0.7 + 0.4*sin(t*2)", (0, 3));
Giving lines some body
A 3D line, arrow, or curve is a thin thread by default. thick turns it into a
rounded tube (arrows grow a solid head):
arrow3(v, (0, 0, 0), (2, 2, 2)); thick(v, 0.04);
Moving in 3D
Same rhythm as the 2D verbs, with the 3 on the end:
par {
rotate3(box, (0, 0, 360), 4, linear); // spin the box
orbit3(70, 25, 12, 4, smooth); // orbit the camera around it
}
move3/shift3— move to / by a pointrotate3— turn it (degrees around x, y, z)grow3— stretch a line or arrow’s tip to a new pointorbit3— swing the camera (angle around, angle up, distance)look3— aim the camera at a new point
Morphing one shape into another
morph3 sets a shape up to become another; then to(..., morph, ...) blends
between them. It works for curves, surfaces, and solids — even a cube turning
into a sphere:
cube3(a, (0, 0, 1), (2, 2, 2));
sphere3(b, (0, 0, 0), 1.2); hidden(b);
morph3(a, b);
to(a, morph, 1, 2.5, smooth); // a cube melts into a ball
Which words work in 3D?
3D shapes speak most of the same vocabulary — color, opacity, hidden,
untraced, tag, and the verbs show, fade, draw, flash, pulse,
scale. A handful of words are 2D-only and will politely refuse on a 3D
shape (with a message that names the 3D replacement):
| if you reach for… | on a 3D shape, use… |
|---|---|
hue | color with a palette name |
stroke | thick |
move / rotate / spin | move3 / rotate3 |
cam / zoom | camera3 / orbit3 |
morph | morph3 |
Now see it all in motion in the 3D scenes gallery.