Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 3cube3, sphere3, move3, orbit3. That’s how you tell the two worlds apart.

Generated and derived 3D geometry

These constructors cover large scientific structures without expanding the scene into thousands of named objects:

implicit3(shell,"x*x+y*y-z*z",(-2,2),(-2,2),(-2,2),1,24);

collection3data(nodes,(0,0,0),"0 0 0; 1 0 0; 1 1 1; 0 1 0");
links3data(edges,nodes,"0 1; 1 2; 2 3; 3 0");

lsystem3(tree,(0,0,0),0.18,25,5,"F","F=F[+F][-F][^F]");
  • implicit3 samples a bounded scalar field and extracts one isosurface.
  • collection3data and links3data retain explicit coordinates and arbitrary edges as two batches—useful for Cayley diagrams, dependency graphs, and imported scientific datasets.
  • lsystem3 is a 3D turtle grammar: F draws, f moves, +/- yaw, ^/& pitch, </> roll, and brackets branch.

To show the three shadows of a moving curve, derive them from the source:

curve3(path,"2*cos(t)","2*sin(t)","t/3",(0,12.56));
projectpath3(xyShadow,path,xy);
projectpath3(xzShadow,path,xz);
projectpath3(yzShadow,path,yz);

par {
  rotate3(path,(20,50,10),2,smooth);
  orbit3(55,20,18,2,smooth);
}

The projected paths resample the source after its current transform, so the shadows cannot drift away from the geometry they explain.

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.

The creator-first five

For most explainers, start with the relationship you want rather than camera coordinates or intermediate positions. 3D V2 adds five composition words:

wordcreator intent
view3(subject, "isometric", 1, smooth, 1.3)frame one object or tagged group, with margin
travel3(subject, route, 2, smooth)move along a line3, arrow3, or curve3
attach3(label, subject, (0,0,1))position-only follow (compatible default); add rigid to inherit orientation; use none to release
become3(subject, blueprint, 1, smooth)keep the subject id while it adopts another shape and style
turn3(group, pivot, z, 90, 1, smooth)rigidly turn one object or tagged group around a pivot and axis

Here is the core pattern. The sensor and wings travel with the ship, release without snapping, then the wings deploy and the ship becomes its final design:

attach3(sensor, ship, (0,0,0.8));
attach3(leftWing, ship, (0,-1.2,0));
attach3(rightWing, ship, (0,1.2,0));
par { draw(route, 2.8, smooth); travel3(ship, route, 2.8, smooth); }

attach3(sensor, none);
attach3(leftWing, none);  attach3(rightWing, none);
par {
  turn3(wings, ship, z, 90, 1.1, smooth);
  become3(ship, finalBlueprint, 1.1, smooth);
}
view3(spacecraft, "isometric", 1.2, smooth, 1.4);

▶ Compact five-word reference:

▶ Vertical creator story:

▶ Production lab (relationships, contours, finishes, OBJ, and variable tube):

Design details that make 3D feel professional

  • Give the main subject one persistent id. Declare alternate forms as hidden blueprints and use become3; do not swap the whole scene.
  • Tag parts that should turn or frame together. view3 and turn3 both accept a tag, so one spatial rig remains one authoring concept.
  • Attach before the shared move; release immediately before a part gets its own motion. Release preserves the resolved world position.
  • Use view3(...,"fit") to preserve the current viewing direction, or choose front, side, top, or isometric. A margin around 1.15–1.35 is useful for landscape; start around 1.45–1.7 for vertical video with text above and a social footer below.
  • Put shot changes at idea boundaries, then let object motion carry the middle of the explanation. Constant camera motion makes depth harder to read.
  • view3 uses transformed group bounds and the active canvas aspect ratio, so the same intent works in landscape and portrait without a second 3D mode. In a creator/quiz scene it also fits the actual media rectangle, not the full canvas behind the heading and social footer.
  • travel3 reads the route’s current transform every frame. A route may rotate or move in the same par block without pre-baking dozens of coordinates.
  • Use attach3(part, body, offset, rigid) for a mechanical assembly. Its offset lives in the body’s local frame, it turns with the body, and release freezes both position and orientation exactly.

▶ See it play:

Scientific frames: 2D, textbook 3D, and spatial 3D

Use the smallest truthful dimension. A genuinely planar geometry problem should stay in Manic’s native 2D shapes. When the relationship is spatial but the result belongs on a calm textbook page, use a real 3D frame3 with an orthographic camera and mode=textbook. When depth becomes part of the lesson, restyle that same frame and orbit it:

camera3((9,-11,8), (0,0,0), 12, orthographic);

frame3(world, (0,0,0), (8,8,6),
  "x=-4..4 y=-4..4 z=-3..3 planes=xy:min,xz:min,yz:min major=2 minor=1 mode=textbook");

// Later: same coordinates, geometry, labels, and identity.
par {
  present3(world, spatial, 0.7, smooth);
  orbit3(52, 28, 12, 2.4, smooth);
}

The options are compact and composable:

needoption
data boundsx=-2..4 y=0..8 z=0.1..100
one or more wallsplanes=xy:min,xz:min,yz:min
a top or central planeplanes=xy:max,xz:origin
an exact sectionplanes=xy:-0.5
two coloured parallel sectionsplanes=xy:-0.5@cyan,xy:1.5@magenta
shared intervalsmajor=1 minor=0.5
independent intervalsxmajor=0.5 ymajor=1 zmajor=2
logarithmic datazscale=log (the corresponding range must be positive)
page or spatial stylingmode=textbook or mode=spatial

Every generated role is addressable through tags: world.axes, world.axis.x, world.grids, world.grid.xy, world.grid.major, world.grid.minor, world.ticks, and world.labels. This makes ordinary show, fade, pulse, and recolor useful without adding graph-specific animation commands.

grid3 and axes3 remain the fastest defaults for a simple positive stage. Choose frame3 when bounds, log scale, multiple walls, repeated sections, or textbook/spatial continuity matter.

▶ Solve the 3 × 4 × 12 box in three honest views:

▶ Pure 3D: shortest distance from a point to a plane:

▶ Six Asymptote-inspired grid policies:

The 3D cast

shapewritedraws
cube3cube3(box, (0,0,1), (2,2,2));a box (width, depth, height)
sphere3sphere3(ball, (0,0,1), 0.9);a ball of that radius
point3point3(p, (1,1,1));a small marker in space
line3 / arrow3arrow3(v, (0,0,0), (0,0,2));a segment / a vector
grid3grid3(floor, (0,0,0), 5, 1);a ground grid to sit things on
axes3axes3(ax, (0,0,0), 3);labelled x, y, z arrows
frame3frame3(world,(0,0,0),(8,8,6),"planes=xy:min,xz:min mode=textbook");bounded scientific axes plus selected grid planes
randomwalk3randomwalk3(walk,(0,0,0),10000,21,"mode=turtle angle=60 color=turn shade=depth");one deterministic 3D path with a local/world direction model
hilbert3hilbert3(curve,(0,0,0),6,4);one exact space-filling path through all cells of a 3D lattice

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), (18, -14)); // optional screen offset clears the marker

pin3 deliberately stays the same screen size. When the label should feel as if it lives in the world, use label3 and give its desired world height:

text(pName, (0,0), "P");
label3(pName, pointP, 0.35);   // gets smaller as P moves away

Relationships, projections, and contours

These are live construction words, not snapshots:

point3(p, (2,1,3));
project3(shadow, p, "xy");
link3(drop, p, shadow, 0.06);

surface3(bowl, "x^2+y^2", (-2,2), (-2,2), 24);
contour3(levelOne, bowl, 1);

Move p and both shadow and drop recompute every frame. contour3 accepts a surface3 height field and extracts the requested z level. Use these words for geometry, vectors, optimization, fields, and engineering callouts—the engine owns the relationship while the author owns the story.

Deterministic 3D random walks

randomwalk3 keeps tens of thousands of decisions in one path, so draw-on, camera fitting, direct seeking, and export remain predictable:

camera3((10,-12,8),(0,0,0),38,orthographic);
randomwalk3(walk,(0,0,0),12000,21,
  "mode=turtle angle=60 distribution=gaussian color=turn shade=depth scale=0.12");
untraced(walk);
par {
  draw(walk,2.4,smooth);
  view3(walk,"fit",0.7,smooth,1.2);
}
  • mode=axis chooses one of ±x, ±y, ±z on every step.
  • mode=turtle advances along a local heading, then turns that orientation frame; use angle=90 or 60 for the classic textbook constructions.
  • distribution=uniform|gaussian changes how the six choices are sampled.
  • color=direction (or turn) paints segments by the selected choice.
  • shade=depth adds a camera-aware far/near cue without changing the path.
  • scale is the world-space length of every step. seed makes comparison and rerendering exact. The bounded maximum is 50,000 steps.

See asymptote-randomwalk3-reference.manic for the recurring Asymptote models and creator-randomwalk3-diffusion.manic for a 100 → 1,000 → 10,000-step explanation.

A line that fills a cube

hilbert3 generates one exact 3D Hilbert path as a single seekable entity. Order 1 visits 8 cells with 7 segments; every refinement replaces each visit with eight smaller visits, so order 5 has 32,767 segments:

camera3((13,-15,11),(0,0,0),42);
hilbert3(curve,(0,0,0),6,1);
hilbert3(next,(0,0,0),6,2); hidden(next);

untraced(curve);
draw(curve,1.5,smooth);
become3(curve,next,1.3,smooth);

The default arc-length gradient stays continuous through become3; choose "color=single" when an ordinary color(curve, cyan) should paint the whole path. Orders are deliberately bounded to 1–5 so direct seeking and production rendering remain predictable. See How one line fills a cube for the full 7 → 32,767-segment story with a continuous camera and CTA.

Large evolving 3D stories

Four small foundations cover dependency clouds, articulated mechanisms, time-varying fields, and addressable model parts without exposing frame callbacks.

IntentManic words
many stable pointscollection3
relationships among themlinks3, child3
one dependent articulated chainchain3, trail3
a changing 3D field and its motionvectorfield3, advect3
a camera that follows one memberfollowshot3
named parts inside one OBJassembly3
a small offline sound beatcue

Here is the reusable field pattern:

camera3((8,-10,6), (0,0,0), 42);
collection3(seeds, (-2,0,0), 48, 1.2, 21, 0.045);
vectorfield3(flow, (0,0,0), 4,
  "-y + 0.35*sin(2*pi*p)",
  "x + 0.25*cos(2*pi*p)",
  "0.25*sin(x+y+2*pi*p)", 5);

child3(hero, seeds, 0, 0.08);
trail3(history, seeds, 0, 0.025);
par {
  advect3(seeds, flow, 5, 0.45);
  followshot3(hero);
}
followshot3(none);

p is normalized absolute time from 0 to 1. Manic precompiles the RK4 paths, then samples those paths, the field arrows, the child proxy, the trail, and the camera target from the same timeline time. Scrubbing backward or jumping straight to the middle is therefore repeatable.

Use collection3 for a fixed-count repeated 3D cast. It renders as a batch, while child3 exposes only the member that needs a label or camera. links3 can create chain, nearest, or all relationships without hundreds of handwritten link3 calls:

collection3(cloud, (0,0,0), 80, 3, 42);
links3(neighborhood, cloud, nearest, 2);
drift3(cloud, 4, 0.6);

For a dependent mechanism, every chain3 endpoint starts where its predecessor ended. The history is taken from the real compiled endpoint route:

collection3(arm, (0,0,0), 4, 0, 7, 0.07);
chain3(arm, "1.8 1.35 1.0", "1.0 -1.8 2.4", 6);
links3(bones, arm, chain);
ring3(firstOrbit, arm, 0, 72);
ring3(secondOrbit, arm, 1, 72);
trail3(tipHistory, arm, 3, 0.035);

ring3 does not approximate a separately animated circle. Its centre is the previous endpoint and its radius is the current distance to the chosen child, so it remains correct while the chain rotates, travels, seeks, or rewinds.

Turn that same member history into a truthful screen-space plot with historyplot. Choose x, y, or z; Manic uses the complete compiled range for stable scaling and reveals only the history reached so far:

historyplot(wave, arm, 3, y, (540,1250), (820,320));

Use historyplot3 when the trace belongs inside the 3-D world rather than the screen overlay. Its origin is a world coordinate and its size is measured in world units, so depth stacking, focus shots, orbiting, and follow cameras all remain coherent:

historyplot3(wave3, arm, 3, y, (-3,-4,2), (8,2.5));
view3(wave3, "front", 1, smooth, 1.2);

Both forms are derived from the same compiled child motion. Use historyplot for a dashboard or fixed teaching panel; use historyplot3 for layered Fourier constructions, spatial signal galleries, and camera-led explanations.

For a procedural tree, tree3 keeps authoring and rendering bounded. Each generation is one addressable edge batch, while leaves are one collection:

tree3(tree, (0,0,0), 2.1, 27, 0.72, 10, 42);
for i in 0..10 { untraced(tree.d{i}); }
stagger(0.12) { for i in 0..10 { draw(tree.d{i},0.5,smooth); } }
view3(tree, "isometric", 1.0, smooth, 1.35);

This is the right tradeoff for creator files: a meaningful generated recipe, normal layers/tags and camera verbs, but no recursive user callback.

For grouped assets, OBJ group names become safe part ids:

assembly3(console, "asset:models/manic-console.obj", (0,0,0), 1.4);
text(screenLabel, (0,0), "signal");
label3(screenLabel, console.screen, 0.28);
cue(chime);

This is deliberately bounded: stable-count collections, formula fields, named OBJ groups, camera-facing labels, and four local cues. GLB node/material hierarchies, arbitrary SFX files, shader callbacks, count-changing children, and true occluding 3D glyph meshes remain future layers.

▶ Living dependency cloud:

▶ Dependent chain and truthful history:

▶ Fourier chain to live derived waveform:

▶ Five depth-layered Fourier families (DefinedMotion animation2.gif test):

▶ Odd harmonics to square-wave partial sum:

▶ Batched fractal-tree growth:

▶ Time-varying field and follow shot:

▶ Addressable assembly, notation, cue, and particle punctuation:

Textbook dimension-story series

These portrait stories use the same 3D engine for a textbook-friendly purpose: start with one familiar object, preserve its identity, and let the next dimension arrive through motion. Each gallery page contains the complete source and its video card.

StoryDimensional journeyTeaching idea
The trapped light beam1D → 2D → 3DDistance grows from 5 to 13 to 85 through nested right triangles.
How space learned to growpoint → line → plane → roomA line sweeps into a surface; the surface lifts into volume.
Length, area, volume1D → 2D → 3DWhy measured units become cm, cm², and cm³.
A point gets an addressx → (x,y) → (x,y,z)Each new axis adds one coordinate to the same point.
The revolving semicirclediameter → curve → sphereA solid can be generated from a lower-dimensional rule.
Statistical dimensionslist → scatter → cloudMore variables require a richer coordinate world.
Dimension reduction3D → 2D → 1DA sphere reveals a section, then the section reveals a diameter.
Watermelon sectionswhole → halves → ¼ + ¾Perpendicular great-circle cuts create meaningful pieces.

Creator pattern: keep the mathematical subject persistent, introduce one axis or section at a time, and hold the settled frame long enough to read the new formula. Use view3(...,"fit") at dimensional boundaries; use ordinary object motion inside each explanation.

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. Write pi*t, never pit (manic reads pit as one unknown word). Same for v*v, not vv.

Make the same generated object change

Use an ordinary parameter, add p to the generated formula, and connect it with bind. Manic resamples the same object instead of replacing it:

parameter(shape, (640,620), 0, 0, 1, "shape", 2);

surface3(world, "0.22*(x*x+y*y)", (-3,3), (-3,3), 34);
bind(shape, world, formula, "0.22*x*x + 0.22*(1-2*p)*y*y");

to(shape, value, 1, 3, smooth);

The same pattern works for every stable formula family:

bind(shape, helix, formula,
  "(1+p)*cos(t)", "(1+p)*sin(t)", "0.3*t");

bind(shape, torus, formula,
  "(3+(0.2+p)*cos(v))*cos(u)",
  "(3+(0.2+p)*cos(v))*sin(u)",
  "(0.2+p)*sin(v)");

This is a useful creator distinction:

  • use bind when one curve/surface remains the subject and its mathematical rule changes continuously;
  • use morph3 when one authored shape becomes a different authored shape.

The bound object keeps its id, resolution, colour, material, transform, and timeline identity. Manic checks the declared parameter range at build time and rejects sampled non-finite formulas before recording. Topology and item count stay fixed, so this is not a per-vertex scripting API. Do not combine morph3 and a generated-family binding on the same target. gradient3, tangentplane3, and volume3 stay attached to that source and resample as it deforms, so the measurement and the surface cannot drift apart.

▶ General three-family reference:

▶ Creator Short — solve the bowl-to-saddle transition:

Design tip: author the constructor formula to match the parameter’s initial value. Then the first reveal and the live journey share the same exact shape, with no opening jump.

Textbook sections: halves, quarters, and the remainder

param3 is also the V2 route for an exact authored section of a curved solid. Bound one parameter to describe only the required half or quarter of the sphere, and add a second bounded surface for the exposed cut face. This keeps the lesson mathematical: the geometry is defined by the section, not hidden by a flat mask.

The watermelon example uses that pattern to compare horizontal and vertical great-circle cuts, then makes two perpendicular cuts and separates a quarter from its three-quarter remainder:

// A sphere quarter: longitude u spans 90°, latitude v spans the full height.
param3(quarter,
  "3*cos(v)*cos(u)", "3*cos(v)*sin(u)", "3*sin(v)",
  (0, pi/2), (-pi/2, pi/2));
finish3(quarter, "shading=smooth material=matte depth=0.25 shadow=0.18");

▶ Animated textbook reconstruction:

Open the complete, copyable source in the 3D scenes gallery.

Practical authoring tips:

  • Keep the outside shell and each exposed section face as separate tagged entities. They can be revealed, shifted, and recoloured as one teaching unit.
  • Use a light paper template and restrained face colours when the goal is a textbook diagram; depth should clarify the construction, not dominate it.
  • Frame each new construction with view3(tag,"fit",...) after the cast changes. The camera then follows the mathematical subject instead of fixed coordinates.
  • V2 supports exact authored sections through bounded param3. A generic verb that cuts any arbitrary solid and automatically creates the resulting pieces is intentionally deferred to V3.

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 profile r(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);

For a horn, vessel, nerve, pipe, or any path whose radius changes, use a normalized radius profile (t=0 start, t=1 end):

curve3(spine, "4*t-2", "sin(6*t)*0.2", "0", (0,1));
tube3(horn, spine, "0.06 + 0.28*t", 14);

One optional render finish

The default is still Manic’s restrained, template-aware diagram rendering. When an object needs a different surface treatment, finish3 keeps the choice in one bounded string:

finish3(globe, "shading=smooth material=metal depth=0.2 shadow=0.2");
finish3(terrain, "mesh=0.25 texture=checker scale=3");
finish3(shell, "material=glass shading=smooth");
  • Start with shading=smooth for spheres/organic surfaces and leave boxes flat.
  • A little mesh clarifies topology; 1 is intentionally strong.
  • depth and shadow are subtle readability controls from 0 to 1, not a replacement for authored lighting.
  • checker and stripes are procedural and deterministic; Manic does not load texture scripts or arbitrary shaders.

Controlled OBJ models

model3(mark, "asset:models/manic-pyramid.obj", (0,0,1), 1.4);
finish3(mark, "shading=smooth material=metal mesh=0.12");

assembly3(console, "asset:models/manic-console.obj", (3,0,0), 1.2);
show(console.screen, 0.4);

model3 reads geometry only: OBJ vertices, polygon faces (triangulated), and lines. It ignores material/script features and enforces file/geometry limits. An asset: URI selects a file packaged with Manic, so it works from the CLI, Docker image, or production backend without a launch-directory assumption or extra flag. An ordinary path such as uploads/my-model.obj still works for a user-owned model, but the UI/backend must provision that file. Use built-in solids when they express the same idea—they remain the lightest option.

Available bundled 3D assets

Stable URIWhat it isGood for
asset:models/manic-pyramid.objSmall generic pyramid OBJLearning model3, a beacon, marker, monument, or placeholder model
asset:models/manic-console.objGrouped console OBJ (base, screen, key)Learning assembly3, part callouts, staged reveals, and technical product stories

Bundled names are intentionally few and predictable. See the full Bundled assets catalog and do not invent an asset: name that is not listed. To add one to Manic itself, place the geometry-only file under assets/models/, document its URI here and in assets/README.md, and add a checked example. The release, Docker, EC2, and playground pipelines copy the complete assets/ directory automatically, so future catalog entries need no per-file deployment rule.

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
  roll3(-20, 4, smooth);                   // bank around the view direction
}
  • move3 / shift3 — move to / by a point
  • rotate3 — turn it (degrees around x, y, z)
  • grow3 — stretch a line or arrow’s tip to a new point
  • orbit3 — swing the camera (angle around, angle up, distance)
  • roll3 — bank the camera around its viewing direction; it can run beside orbit3 in par, including through stable overhead/underside views
  • look3 — aim the camera at a new point

These remain the precise controls. Use them when exact coordinates or a specific orbit are part of the explanation; use the creator-first five for camera composition and relationship choreography.

Check the transitions, not only the last frame

manic check examples/three-d-v2-lab.manic --canvas portrait

The publishing audit samples camera transitions between named steps. It warns when projected 3D bounds leave the creator media rectangle, an orbit/zoom reads as a shock, the eye enters geometry, or a live spatial relationship has lost its source. It also checks the settled frame of every step. The most useful repair is usually semantic: tag the shot’s subject and call view3(tag,"fit") after the cast changes, or give a camera beat more duration.

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…
huecolor with a palette name
strokethick
move / rotate / spinmove3 / rotate3
cam / zoomcamera3 / orbit3
morphmorph3

Now see it all in motion in the 3D scenes gallery.