Wave3D Handbook
v0.1

Learn by building

Three small scenes that grow one idea at a time: compose an artwork, make it respond, then give it a landscape.

Projects: 3 Format: complete main.ts files Evidence: declaration-checked Runtime status: not claimed
On this page
  1. How to use the projects
  2. Project 1 · Pocket Gallery
  3. Project 2 · Kinetic Exhibit
  4. Project 3 · Terrain Sketch
  5. Reactive Park plan
  6. Official sources

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

  1. Open WaveStudio. Create or open a learning project and select its main.ts file.
  2. Copy one complete project. Replace the current contents of main.ts; do not stack all three examples in the same file.
  3. Read errors before running. WaveStudio’s TypeScript feedback is the quickest way to catch a misspelled method or misplaced argument.
  4. Run the current scene. The exact button names and preview controls are editor UI, so they may change.
  5. Change one value. Run again and describe what changed. That comparison is the lesson.

1 · Pocket Gallery

Constructors, names, color, placement, and composition.

Start project →

2 · Kinetic Exhibit

The same gallery gains a delayed change and keyboard input.

Add behavior →

3 · Terrain Sketch

A runtime noise brush becomes the ground for a reactive park marker.

Shape a world →

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.

You will practice
  • using the exact after(value, unit).do(callback) shape;
  • registering myScene.director.whenPress with Keyboard.Space;
  • changing existing entities from callbacks.

Declaration-checked example · not runtime-tested

Project 2 · complete main.ts
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
  1. Change the delay from 2 to 4. Which value is the amount, and which value is the unit?
  2. Change Keyboard.Space to Keyboard.ArrowRight. Which official enum supplies those names?
  3. Replace turnRight(30) with turnLeft(30). Why is turn(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.

You will practice
  • 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

Project 3 · complete main.ts
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
  1. Change amount(6) to amount(3). Make a prediction, then compare the scene.
  2. Change radius(640) to radius(320). Which part of the chain describes where the brush begins?
  3. Move the bench with moveForward(2). How is that different from changing the numeric placeAt arguments?

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.

MilestoneLearning questionOfficial surface to investigate
1 · Arrange landmarksHow do names and positions make a scene readable?setName, transform methods, WaveScene.getByName
2 · Group a groveHow can several entities be managed together?WaveScene.createGroup, WaveGroup
3 · Add a responseWhat should happen after time or input?WaveEntity.after, waveSceneDirector.whenPress
4 · Add atmosphereHow should sky, weather, fog, and light support the artwork?WaveScene.sky, weather, fog, and lighting declarations
5 · Add imported artHow does a project asset become a scene object?WaveScene.assets, project asset accessors, model-use methods
6 · Make it physicalWhich 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

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.

This independent learning handbook is based on Wave3D’s official public pages and TypeScript declarations. Declaration compatibility is not a claim of runtime or visual verification.

Search the tutorial, guide, API reference, and examples.