Learn by building
Three small scenes that grow one idea at a time: compose an artwork, make it respond, then give it a landscape.
On this page
What “declaration-checked” means here: every class, method, global, enum member, and argument shape shown below matches Wave3D’s published TypeScript declarations. These scenes have not been represented as Clean-Run or visually verified in WaveStudio. Treat the “expected result” notes as learning targets, not test reports.
Observed editor workflow
How to use the projects
- Open WaveStudio. Create or open a learning project and select its
main.tsfile. - Copy one complete project. Replace the current contents of
main.ts; do not stack all three examples in the same file. - Read errors before running. WaveStudio’s TypeScript feedback is the quickest way to catch a misspelled method or misplaced argument.
- Run the current scene. The exact button names and preview controls are editor UI, so they may change.
- Change one value. Run again and describe what changed. That comparison is the lesson.
3 · Terrain Sketch
A runtime noise brush becomes the ground for a reactive park marker.
Shape a world →Project 1 · Static composition
Pocket Gallery
Build a tiny exhibition from the three canonical beginner primitives. The floor establishes scale; the sphere, cylinder, and cube become artworks.
- constructing
waveCube,waveSphere, andwaveCylinder; - reading constructor arguments in order;
- chaining
setName,setColor,placeAt, andturnRight.
Declaration-checked example · not runtime-tested
const galleryFloor = new waveCube(10, 0.5, 6)
.setName("GalleryFloor")
.setColor(PALETTE.WHITE)
.placeAt(0, 0, 0);
const cyanOrb = new waveSphere(1.25, 24)
.setName("CyanOrb")
.setColor(PALETTE.CYAN)
.placeAt(-3, 1.5, 0);
const pinkTower = new waveCylinder(3, 1.2, 1.2, 24)
.setName("PinkTower")
.setColor(PALETTE.PINK)
.placeAt(0, 1.75, 0);
const orangeBox = new waveCube(1.8, 1.8, 1.8)
.setName("OrangeBox")
.setColor(PALETTE.ORANGE)
.placeAt(3, 1.15, 0)
.turnRight(25);
myScene.print("Pocket Gallery", 18, PALETTE.WHITE);
Read the composition
The first cube is wide, short, and deep because its arguments are width, height, then depth. The cylinder’s first argument is its height; the next two are its top and bottom diameters. Every placeAt uses a different x coordinate to make a left–middle–right arrangement.
Tutorial guidance · expected result
You should be aiming for a low rectangular floor with three colored forms spaced across it. If objects overlap, first compare the numbers in their placeAt calls.
Remix lab and check-yourself questions
- Change
PALETTE.ORANGEtoPALETTE.CYAN. Which line controls the box’s appearance? - Add
.moveLeft(1)after the orb’splaceAt. Which coordinate-direction idea does that reinforce? - Change only the first cylinder argument from
3to4. Predict the part that should change before running.
Official declarations used: waveCube, waveSphere, waveCylinder, WaveScene.print, and PALETTE.
Project 2 · Time and input
Kinetic Exhibit
Keep the Pocket Gallery, then add two kinds of “later”: one change happens after two seconds, and another happens whenever the visitor presses Space.
- using the exact
after(value, unit).do(callback)shape; - registering
myScene.director.whenPresswithKeyboard.Space; - changing existing entities from callbacks.
Declaration-checked example · not runtime-tested
const galleryFloor = new waveCube(10, 0.5, 6)
.setName("GalleryFloor")
.setColor(PALETTE.WHITE)
.placeAt(0, 0, 0);
const cyanOrb = new waveSphere(1.25, 24)
.setName("CyanOrb")
.setColor(PALETTE.CYAN)
.placeAt(-3, 1.5, 0);
const pinkTower = new waveCylinder(3, 1.2, 1.2, 24)
.setName("PinkTower")
.setColor(PALETTE.PINK)
.placeAt(0, 1.75, 0);
const orangeBox = new waveCube(1.8, 1.8, 1.8)
.setName("OrangeBox")
.setColor(PALETTE.ORANGE)
.placeAt(3, 1.15, 0)
.turnRight(25);
cyanOrb.after(2, Seconds).do((target) => {
target.moveUp(1).turnRight(45).setColor(PALETTE.ORANGE);
});
myScene.director.whenPress(Keyboard.Space, () => {
pinkTower.turnRight(30);
orangeBox.scaleTo(1.25);
});
myScene.print(
"Wait for the orb, then press Space!",
18,
PALETTE.WHITE,
);
Two callbacks, two triggers
The delayed callback receives the entity as target. The keyboard callback closes over the named entities from the surrounding file. In both cases, the code inside braces runs later—not while the scene file is first read.
Use after(2, Seconds). Calling Seconds(2) produces a WaveTimePoint, which is not the same type as the one-argument after(duration: WaveTimeSpan) overload.
Tutorial guidance · expected result
You should be aiming for the same gallery, followed by one delayed orb change. Pressing Space should request a 30-degree turn for the tower and set the box’s scale target to 1.25.
Remix lab and check-yourself questions
- Change the delay from
2to4. Which value is the amount, and which value is the unit? - Change
Keyboard.SpacetoKeyboard.ArrowRight. Which official enum supplies those names? - Replace
turnRight(30)withturnLeft(30). Why isturn(30)not an equivalent spelling?
Official declarations used: WaveEntity.after, DelayedActionSurface.do, waveSceneDirector.whenPress, Keyboard, and Seconds.
Project 3 · World system
Terrain Sketch
Move from arranging objects to shaping their setting. This project begins with the runtime noise-brush chain found in WaveStudio’s shipped starter material, then places a few park-like forms above it.
- navigating a scene facade and runtime builder;
- finishing a terrain brush with
apply(); - combining terrain, primitives, time, and input in one file.
Declaration-checked example · not runtime-tested
myScene.terrain.runtime
.noise()
.at({ x: 0, z: 0 })
.radius(640)
.amount(6)
.frequency(0.015)
.octaves(3)
.falloff("smooth")
.apply();
const parkBeacon = new waveCylinder(4, 0.6, 1.4, 24)
.setName("ParkBeacon")
.setColor(PALETTE.CYAN)
.placeAt(0, 8, 0)
.turnRight(20);
const treeTrunk = new waveCylinder(3, 0.7, 0.9, 20)
.setName("TreeTrunk")
.setColor(PALETTE.ORANGE)
.placeAt(-3, 8, 0);
const treeCanopy = new waveSphere(1.6, 24)
.setName("TreeCanopy")
.setColor(PALETTE.PINK)
.placeAt(-3, 10, 0)
.setScale(1.4, 0.7, 1.4);
const parkBench = new waveCube(3, 0.5, 1)
.setName("ParkBench")
.setColor(PALETTE.WHITE)
.placeAt(3, 8, 0);
parkBeacon.after(2, Seconds).do((target) => {
target.moveUp(1).setColor(PALETTE.ORANGE);
});
myScene.director.whenPress(Keyboard.Space, () => {
parkBeacon.turnRight(30);
});
myScene.print(
"Reactive Park: press Space to turn the beacon",
16,
PALETTE.WHITE,
);
Read the builder before changing it
myScene.terrain.runtime reaches the runtime terrain facade. noise() creates a WaveTerrainBrushBuilder. The middle calls configure that builder and return this. Finally, apply() returns the terminal scene type.
Tutorial guidance · expected result
You should be aiming for a noise-shaped terrain area with a beacon, a simple two-part tree, and a bench-like block near the center. Their y values are a starting composition choice, not a guarantee that every object rests exactly on the generated surface.
Remix lab and check-yourself questions
- Change
amount(6)toamount(3). Make a prediction, then compare the scene. - Change
radius(640)toradius(320). Which part of the chain describes where the brush begins? - Move the bench with
moveForward(2). How is that different from changing the numericplaceAtarguments?
Official declarations used: WaveScene.terrain, WaveTerrainFacade.runtime, WaveTerrainRuntimeFacade.noise, and WaveTerrainBrushBuilder.
Project planning guidance
Turn the sketch into a Reactive Park
The third project is a foundation, not a finished park. Expand it in small, testable slices. At each slice, locate the exact declaration before writing the call.
| Milestone | Learning question | Official surface to investigate |
|---|---|---|
| 1 · Arrange landmarks | How do names and positions make a scene readable? | setName, transform methods, WaveScene.getByName |
| 2 · Group a grove | How can several entities be managed together? | WaveScene.createGroup, WaveGroup |
| 3 · Add a response | What should happen after time or input? | WaveEntity.after, waveSceneDirector.whenPress |
| 4 · Add atmosphere | How should sky, weather, fog, and light support the artwork? | WaveScene.sky, weather, fog, and lighting declarations |
| 5 · Add imported art | How does a project asset become a scene object? | WaveScene.assets, project asset accessors, model-use methods |
| 6 · Make it physical | Which objects should be fixed, dynamic, or interactive? | Physics body methods and range/trigger declarations |
Do not guess the advanced syntax from the milestone names. The complete declaration corpus is large and often has several related builders. Use editor completion and the raw .d.ts file to choose the exact surface for your scene.
Official public sources
Sources and verification boundary
- WaveStudio public editor
- Wave3D engine overview
- WaveStudio global TypeScript declarations
- WaveStudio-only entity aliases
- Wave3D Agent SDK README, version 0.3.14—automation and agent workflow context, not a replacement for the authoring declarations.
The terrain chain is also present in the public WaveStudio application’s shipped starter material. The TypeScript signatures above are independently present in the published global declaration file.