The Wave3D Tutorial
Learn the core language by building a tiny interactive sculpture. Each chapter adds one idea, one experiment, and one way to check your understanding.
Chapters on this page
Before you begin
Open WaveStudio in a modern browser. This tutorial refers to the editor and its run action in general terms because the public UI can change. Save any scene you care about before replacing code. Treat each lesson block as a separate experiment unless it explicitly says to combine them.
- Type, do not only paste.
Typing helps you notice punctuation and lets editor suggestions teach you the available names. - Run the smallest version first.
Once it works, change one number, color, or line. - Read the first error.
A later error may be caused by one missing quote or parenthesis near the top. - Keep evidence labels in mind.
“Declaration-checked” means type-compatible with the captured public API, not that this page has run your exact Studio build.
Vocabulary. A scene is the world being authored. An object is one item in it. A method is an action or setting reached after a dot, such as .setColor(...).
Chapter 1
Your first object
- Create a canonical Wave3D cube.
- Name, color, and place the cube.
- Explain what a constructor argument changes.
A primitive is a basic 3D form. Wave3D exposes canonical classes for several primitives. To create one, write new, the class name, and parentheses containing its starting measurements.
Official API
constructor(
width?: number,
height?: number,
depth?: number,
widthSegments?: number,
heightSegments?: number,
depthSegments?: number
);
setName(name: string): this
setColor(color: Parameters<cmp_visual3DModel["setColor"]>[0]): this
placeAt(x: number, y: number, z: number): this
Declaration-checked example
const block = new waveCube(2, 2, 2)
.setName("blue-block")
.setColor(PALETTE.BLUE)
.placeAt(0, 1, 0);
What each line says
const blockcreates a code label for this object.new waveCube(2, 2, 2)requests width 2, height 2, and depth 2.setName("blue-block")gives the scene entity a readable name.PALETTE.BLUEuses one of the declared palette keys.placeAt(0, 1, 0)sets x, y, and z coordinates.
Tutorial guidance
Lab: make a tall block
- Run the example unchanged.
- Change the constructor to
new waveCube(2, 4, 2). - Change the color to another declared key, such as
PALETTE.CORAL. - Change the y coordinate if you want to reposition the taller object.
Expected result
You should see one colored box. After the lab it should be taller, and the palette change should be visible. Exact framing and lighting depend on the current scene and Studio defaults.
Common mistakes
- Leaving out
newbefore the canonical class. - Writing
wavecube; TypeScript names are case-sensitive. - Forgetting quotes around
"blue-block". - Inventing a palette name. Type
PALETTE.and use the editor’s suggestions. - Assuming the Studio-only
cube()alias is the canonical public class. This handbook usesnew waveCube(...).
Check yourself
- Which three measurements come first in the
waveCubeconstructor? - Which value in
placeAt(0, 1, 0)is y? - Why does
PALETTE.BLUEnot use quotation marks?
Answers: width, height, depth; the middle value; it is a declared property lookup, not a text literal.
Sources for this chapter
- Official global declarations:
waveCube,waveGeometry.setColor, entity naming, transforms, andPALETTE. - Official Studio alias declarations: convenience alias boundary.
Chapter 2
Reading Wave TypeScript
- Recognize variables, types, functions, arguments, and method chains.
- Read a public signature without being intimidated by it.
- Use a small typed helper function.
WaveStudio code is TypeScript. TypeScript is JavaScript with a vocabulary for describing what kinds of values are allowed. In a signature such as moveUp(distance: number): this, the parameter named distance expects a number, and the method returns the current object.
| Code | Read it as |
|---|---|
const marker | Make a variable called marker; do not reassign it. |
: waveCube | The value is expected to be a waveCube. |
new waveCube(1, 1, 1) | Construct a cube using three numeric arguments. |
.setColor(...) | Call a method on the object to the left of the dot. |
amount: number | A parameter called amount must receive a number. |
: void | The function is used for its action and does not promise a returned value. |
Declaration-checked example
const marker: waveCube = new waveCube(1, 1, 1)
.setName("marker")
.setColor(PALETTE.YELLOW)
.placeAt(0, 0.5, 0);
function lift(object: waveCube, amount: number): void {
object.moveUp(amount);
}
lift(marker, 2);
Why chains work
Many Wave authoring methods declare : this as their return type. That means the call hands the same object back, ready for another dot. These two forms express the same sequence:
// Chained
marker.setColor(PALETTE.YELLOW).moveUp(1);
// Step by step
marker.setColor(PALETTE.YELLOW);
marker.moveUp(1);
Tutorial guidance
Lab: write a reusable move
Change lift(marker, 2) to lift(marker, 4). Then rename the function to raise in both its definition and call. Finally, remove the explicit : waveCube on the variable and notice that the editor can infer the constructor’s result.
Expected result
The yellow marker moves higher when you pass a larger amount. Renaming the function changes no behavior as long as both names match.
Common mistakes
- Changing the function’s name but not the call below it.
- Passing text such as
"two"where anumberis required. - Reading
thisas a parameter. It appears after the colon because it is the return type. - Assuming every API returns
this. Check the signature before chaining.
Check yourself
- What does the colon mean in
amount: number? - Why can
setColor(...).moveUp(...)be chained? - What does
voidtell you aboutlift?
Answers: it introduces the allowed type; setColor returns the object; lift is called for its action and promises no result value.
Sources for this chapter
- Official global declarations: return types for entity, geometry, and transform methods.
Chapter 3
Geometry & composition
- Choose among cube, sphere, and cylinder primitives.
- Use exact constructor argument order.
- Compose several simple objects into one recognizable artwork.
Complex 3D art often begins as a collection of simple forms. A sphere can become a head, a cylinder a body, and a flattened cube a platform. Composition is the art of choosing their proportions and positions so the viewer reads them as one idea.
Official API
// waveCube
constructor(
width?: number,
height?: number,
depth?: number,
widthSegments?: number,
heightSegments?: number,
depthSegments?: number
);
// waveSphere
constructor(radius?: number, segments?: number);
// waveCylinder
constructor(
height?: number,
diameterTop?: number,
diameterBottom?: number,
tessellation?: number,
heightSegments?: number
);
Read cylinder order carefully. The first value is height, followed by top diameter and bottom diameter. Do not silently substitute a radius-based order from another 3D library.
Declaration-checked example
const platform = new waveCube(6, 0.5, 6)
.setName("platform")
.setColor(PALETTE.SLATE)
.placeAt(0, -0.25, 0);
const body = new waveCylinder(3, 1.5, 1.5, 32)
.setName("robot-body")
.setColor(PALETTE.CYAN)
.placeAt(0, 1.5, 0);
const head = new waveSphere(1, 32)
.setName("robot-head")
.setColor(PALETTE.YELLOW)
.placeAt(0, 3.8, 0);
Tutorial guidance
Lab: turn the robot into a lighthouse
- Change the body’s bottom diameter to
2.2while keeping its top diameter1.5. - Change the body color to
PALETTE.WHITE. - Change the head color to
PALETTE.RED. - Move the head up or down until it sits where you want.
Expected result
Three separate primitives should read together as a small character. After the lab, the wider cylinder base and red sphere should suggest a stylized lighthouse.
Common mistakes
- Using radius values for
waveCylindereven though its constructor asks for diameters. - Giving two top-level constants the same name in one experiment.
- Putting every object at exactly the same coordinates and hiding one inside another.
- Expecting names alone to group objects. Here the forms are a visual composition, not yet a
WaveGroup.
Check yourself
- What are the first three cylinder arguments?
- Which primitive would you choose for a ball?
- What should you check in Studio after placing the platform at a negative y value?
Answers: height, top diameter, bottom diameter; waveSphere; check how the platform aligns with the other forms. The negative y value is an intended composition choice, and the declarations do not specify a primitive’s default pivot.
Sources for this chapter
- Official global declarations:
waveCube,waveSphere, andwaveCylinderconstructors.
Chapter 4
Spatial language & transforms
- Distinguish placing from moving.
- Use readable directional transform verbs.
- Turn and scale an object without inventing generic method names.
A transform describes position, rotation, and scale. placeAt puts an object at a target coordinate. Movement verbs change where it is from its current state. Turning changes orientation. Scaling changes size.
Official API
| Task | Selected exact signatures |
|---|---|
| Place | placeAt(x: number, y: number, z: number): this |
| Move | moveForward(distance: number): thismoveBackward(distance: number): thismoveLeft(distance: number): thismoveRight(distance: number): thismoveUp(distance: number): thismoveDown(distance: number): this |
| Turn | turnLeft(degrees: number): thisturnRight(degrees: number): thisturnTo(target: TransformReferenceLike | WavePointInput): thisturnToward(direction: DirectionInput): this |
| Scale | scaleBy(value: number | Vector3Like): thisscaleTo(value: number | Vector3Like): thissetScale(x: number, y: number, z: number): this |
Declaration-checked example
const tower = new waveCube(2, 3, 2)
.setName("moving-tower")
.setColor(PALETTE.PURPLE)
.placeAt(0, 1.5, 0);
tower
.moveRight(3)
.moveUp(1)
.turnLeft(45)
.scaleBy(0.75);
There are no beginner generic methods named move(), turn(), or scale() in the verified core surface. Use the declared verbs, such as moveRight, turnLeft, and scaleBy.
By, to, and set
The names signal intent: by usually describes a change from the current value, while to or set describes a target value. That reading is a useful authoring rule, but the exact runtime frame and animation behavior depend on the selected overload and current Wave3D implementation.
Tutorial guidance
Lab: choreograph four transforms
- Run only the constructor chain and observe the starting placement.
- Add
tower.moveLeft(2);and run again. - Add
tower.turnRight(90);. - Finish with
tower.setScale(1, 2, 1);to use different x, y, and z scale values.
Expected result
The tower starts at one known coordinate, then moves, turns, and changes size in the order your calls are evaluated. With setScale(1, 2, 1), it should be stretched along one scale component.
Common mistakes
- Confusing
placeAtwithmoveBy. One targets a place; the other expresses a change. - Writing
turnLeft(0.5)while thinking in radians. The signature names the unitdegrees. - Using an undeclared generic method because another 3D library has one.
- Changing several transforms at once before checking which call caused an unexpected result.
Check yourself
- Which method expresses an exact x, y, z placement?
- What unit does
turnRightname? - Which call would make all scale components 2?
Answers: placeAt; degrees; setScale(2). This overload explicitly names its number parameter uniform.
Sources for this chapter
- Official global declarations:
cmp_transformplacement, movement, rotation, and scale overloads.
Chapter 5
Time & delayed actions
- Schedule an action with
after(value, unit). - Use the callback target safely.
- Avoid confusing a time unit with a time point.
A delayed action has two parts: a delay and the work to do after that delay. Wave entities expose an after proxy whose do callback receives the target entity.
Official API
after(value: number, unit: WaveTimeUnit): DelayedEntityProxy<this>
interface DelayedActionSurface<TEntity extends object> {
do(callback: (entity: TEntity) => void): TEntity;
}
type DelayedEntityProxy<TEntity extends object> =
TEntity & DelayedActionSurface<TEntity>;
declare const Seconds: {
(value: number): WaveTimePoint;
readonly kind: WaveTimeUnitKind;
};
Declaration-checked example
const signal = new waveCube(1, 3, 1)
.setName("signal")
.setColor(PALETTE.BLUE)
.placeAt(0, 1.5, 0);
signal.after(1, Seconds).do((target) => {
target.moveUp(2);
});
signal.after(2, Seconds).do((target) => {
target.turnRight(45).setColor(PALETTE.ORANGE);
});
Use after(2, Seconds) for a two-second span. Do not teach after(Seconds(2)) as the equivalent: calling Seconds(2) is declared to produce a WaveTimePoint, not a duration span.
Read the callback
The arrow => introduces a callback. Wave calls that function later and supplies target. Inside the braces, target is the entity whose delayed proxy you created. Choosing the name target is a readable habit, not a required keyword.
Tutorial guidance
Lab: make a three-beat signal
Add a third delayed action at three seconds. In it, move the target down by 2 and change it to PALETTE.GREEN. Before running, write down what you expect at 0, 1, 2, and 3 seconds.
Expected result
The signal begins blue. One scheduled callback moves it upward; another turns it and changes it to orange. Your third callback should move it down and turn it green. Exact timing should be confirmed in the current Studio.
Common mistakes
- Writing
Seconds(2)where the overload expects a span. - Leaving out
.do(...), so no delayed action is supplied. - Using
signalinside every callback whentargetmakes ownership clearer. - Expecting a sequence just because calls appear on later lines. Each
aftercall states its own delay.
Check yourself
- Which argument is the number in
after(2, Seconds)? - When does the callback body run?
- What type does
Seconds(2)declare?
Answers: the delay value; after the requested delay; WaveTimePoint.
Sources for this chapter
- Official global declarations: entity
after,DelayedEntityProxy,DelayedActionSurface, andSeconds.
Chapter 6
Keyboard interaction
- Connect a declared keyboard key to a callback.
- Use the scene director’s direct
whenPressform. - Build a small controllable object.
Interaction turns a static scene into something a visitor can influence. The scene director declares key events, including whenPress. Its first argument accepts Keyboard | undefined; the examples intentionally use a Keyboard member. Its second argument is a callback.
Official API
myScene.director.whenPress(
key: Keyboard | undefined,
callback: InputCallback<KeyboardInputData, this>,
options?: Omit<CallbackOptions, "filter">
): CallbackHandle
Declaration-checked example
const player = new waveCube(1, 1, 1)
.setName("player")
.setColor(PALETTE.LIME)
.placeAt(0, 0.5, 0);
myScene.director.whenPress(Keyboard.ArrowUp, () => {
player.moveForward(1);
});
myScene.director.whenPress(Keyboard.ArrowLeft, () => {
player.turnLeft(15);
});
myScene.director.whenPress(Keyboard.ArrowRight, () => {
player.turnRight(15);
});
myScene.print("Arrow keys move and turn", 18, PALETTE.WHITE);
The public declarations also type myScene.director.sensing.whenPress(...). This tutorial uses the shorter direct myScene.director.whenPress(...) form as its core pattern.
Tutorial guidance
Lab: add a jump key
Add this fourth handler. Then run the scene, click or focus the scene view if needed, and press Space.
myScene.director.whenPress(Keyboard.Space, () => {
player.moveUp(1);
});
Expected result
Each press triggers its matching callback. Up Arrow calls moveForward(1), Left and Right Arrow turn the object by 15 degrees, and Space moves it upward by one unit. The declarations do not state the coordinate frame used by moveForward. Input focus and capture behavior are Studio UI concerns and may vary.
Common mistakes
- Using
"Space"as plain text instead of the declaredKeyboard.Spaceenum member. - Calling the movement immediately instead of putting it inside the callback braces.
- Needing explicit hold behavior but only inspecting
whenPress. The director separately declareswhenHolding; the declarations do not specify key-repeat behavior forwhenPress. - Testing while keyboard focus is still in the code editor rather than the scene view.
Check yourself
- Which object receives the key event registrations?
- Where must movement code go so it waits for a press?
- Which declared key represents the space bar?
Answers: myScene.director; inside the callback; Keyboard.Space.
Sources for this chapter
- Official global declarations:
waveSceneDirector.whenPress,cmp_sensing.whenPress,Keyboard, andWaveScene.print.
Core path project
Build a moving color sculpture
- Combine primitive geometry, transforms, delayed behavior, keyboard input, and scene text.
- Predict the sculpture’s state before running it.
- Personalize the result without adding unfamiliar APIs.
This project deliberately uses only the vocabulary from Chapters 1–6. Start it as a fresh experiment so earlier constants and event handlers do not collide.
Declaration-checked project
const plinth = new waveCylinder(0.5, 5, 5, 32)
.setName("sculpture-plinth")
.setColor(PALETTE.SLATE)
.placeAt(0, 0.25, 0);
const dancer = new waveCube(1.5, 4, 1.5)
.setName("color-dancer")
.setColor(PALETTE.CYAN)
.placeAt(0, 2.5, 0);
const crown = new waveSphere(1, 32)
.setName("sculpture-crown")
.setColor(PALETTE.GOLD)
.placeAt(0, 5.3, 0);
dancer.after(2, Seconds).do((target) => {
target.turnRight(45).setColor(PALETTE.MAGENTA);
});
myScene.director.whenPress(Keyboard.Space, () => {
dancer.moveUp(1).turnLeft(15);
crown.moveUp(1);
});
myScene.print("Press Space to lift the sculpture", 18, PALETTE.WHITE);
Build checklist
- Run the static geometry.
Temporarily stop before the delayed action and confirm that three forms read as one sculpture. - Add the two-second change.
Predict which object will turn and change color, then run it. - Add Space interaction.
Confirm which two objects move and which one turns on each press. - Personalize one dimension and two colors.
Keep a copy of the working version so you can compare.
Expected result
A short cylinder supports a tall cube and a sphere. After two seconds, the cube should turn and become magenta. Each Space press should lift both upper forms, while the cube also turns left. The text prompt should appear in the scene.
Challenge extensions
- Add Arrow Left and Arrow Right handlers that turn the crown.
- Schedule a third color after four seconds.
- Make a second sculpture at a different x coordinate, using different constant names.
- Write a function that accepts a
waveCubeand a numeric lift amount.
Debug checklist
- Is every opening parenthesis, brace, and quote closed?
- Does each constant have a unique code name?
- Do palette keys come from editor suggestions?
- Is the delay written
after(2, Seconds)? - Are input actions inside callback braces?
Project reflection
- Which three lines establish the initial spatial composition?
- Which work happens automatically, and which waits for the visitor?
- If the sphere and cube separate after several presses, what change would you make?
There is more than one good artistic answer. The important skill is tracing each behavior to the exact object and callback that owns it.
Chapters 7–14
Continue on the advanced track
The public WaveStudio declarations contain thousands of entries across rendering, assets, UI, physics, sensing, world systems, XR, audio, and more. A responsible first edition should not pretend that a signature dump is a finished tutorial. The map below tells you what to study next and where this handbook has enough evidence to help.
7 · Color, material, light & camera
Begin with declared palette keys and setColor. Then study how material editors, lighting configurators, and camera controls change the reading of the same geometry.
8 · Names, groups & layout
Use stable names for debugging and retrieval. Progress from visual composition to declared grouping and scene layout helpers such as inline, grid, path, and around placement.
Open reference →9 · Animation vocabulary
Delayed actions are only the beginning. The declarations expose snapshot and animate overloads, continuous motion, transitions, curves, and lifecycle handles.
Browse reference →10 · Runtime UI
myScene.print is the smallest feedback surface. The broader UI declarations cover panels, text, buttons, sliders, toggles, input, canvas, styles, and screen media.
11 · Assets
Learn the difference between generated primitives and loaded models, textures, materials, audio, video, fonts, and other catalogued assets. Validate identifiers against the current Studio maps.
Open reference →12 · Physics & ranges
Build only after you can separate visible geometry, collision, motion intent, and observation. Range and trigger APIs add spatial conditions to behavior.
Open reference →13 · Atmosphere & world systems
Terrain, sky, weather, water, volumetric fog, post effects, and world plans shape an entire scene rather than a single prop.
See terrain starter ↓14 · Lifecycle & debugging
Learn setup/main boundaries, hot reload, object lifecycle, cleanup handles, naming, and tool overlays. Runtime claims need current-Studio testing.
Open reference →Observed in WaveStudio’s shipped terrain starter
A first world-system recipe
This exact chain was found in the JavaScript shipped by the public WaveStudio app. It is included as an observed starter, not as proof of every terrain runtime behavior.
myScene.terrain.runtime
.noise()
.at({ x: 0, z: 0 })
.radius(640)
.amount(6)
.frequency(0.015)
.octaves(3)
.falloff("smooth")
.apply();
Advanced boundary. Public declarations can prove that a symbol and overload exist. They cannot by themselves prove visual defaults, performance characteristics, asset availability, required permissions, or current editor workflow. Advanced projects should record both a declaration check and a Clean-Run result.
How to study a new API safely
- Find the exact symbol.
Use the curated reference, then follow its raw declaration source. - Read every overload.
Argument order, optional values, and return types often reveal the intended chain. - Make the smallest experiment.
Create one object and call one unfamiliar method. - Clean-Run and observe.
Write down Studio version/date, code, result, and any error. - Add one concern at a time.
Only then combine assets, animation, input, physics, or UI.
Sources for the advanced map
- Official global declarations: complete captured public TypeScript surface.
- Official alias declarations: Studio conveniences and canonical targets.
- Public WaveStudio app: current authoring environment and shipped starter material.
- Published agent SDK README: automation and agent-facing package context.
What to do next
If the moving sculpture worked, you have enough vocabulary to study independently. Choose one next step:
- Use the Language & Authoring Guide to strengthen your mental model.
- Keep the Core API Reference open beside WaveStudio while experimenting.
- Build one of the declaration-checked projects from start to finish.
- Return to the official source list when an exact or advanced behavior matters.