Skip to content
Dashpop

Three.js: take a scene from spinning cube to small game

A scene can look alive while nothing in it is playable. Give the player an action, a consequence, and a reason to use that action again.

By Dashpop5 min read
In this guide

An original workshop for JavaScript developers with a working Three.js scene. It is an external browser-development workflow. Three.js renders the scene; you choose how to implement the game rules, input, and collision.

Build a delivery room before building a world

Start with a floor, a controllable courier shape, three parcel shapes, and a mailbox. Use a fixed camera. Collect one parcel, carry it to the mailbox, and repeat until all three are delivered. Show a carried indicator and delivered count outside the action area. Restart returns everything to its initial state.

This project deliberately uses simple ground-plane movement. No slopes, jumping, stacked collisions, or physics solver are needed for the first experiment. You can model a parcel pickup with a distance rule on that plane and a delivery zone with a rectangle. Those rules suit this room; they are not a general collision engine.

Write the coordinate convention down. For example, use X and Z for the floor and Y for height. Keep movement, pickup checks, and the camera description consistent with that choice. Mixing screen coordinates and world coordinates is a remarkably effective way to make a parcel impossible to collect.

Keep the game state separate from the picture

Track the courier's position, which parcel is carried, which parcels are delivered, and whether the attempt is finished. Use that state to update the scene and interface. A hidden mesh should not be the only record that an item was collected. A missing object can be a rendering issue; the game still needs to know what happened.

In each update, read input, move the courier, apply the room boundaries, resolve eligible collection or delivery, then update the visible result. Keep that order stable. A parcel accepted at the mailbox must stop being carried before another update can count it again.

For keyboard input, track pressed directions and use elapsed time for movement. Normalize diagonal input if equal speed in every direction is intended. Decide what happens when the browser tab loses focus: clear held input and avoid applying a giant movement step on return. The same principle applies to a touch control whose pointer is cancelled.

A camera is part of the rules the player perceives

Three.js cameras define what is visible. A perspective camera's field of view, aspect, and clipping range affect that view. Update the relevant camera projection when the canvas changes size. Keep the renderer dimensions aligned with the displayed canvas. The official camera guide explains these relationships in detail.

For our room, test a narrow screen early. Can the courier still see the mailbox while carrying a parcel? If the destination disappears, decide whether to change the framing, move the interface, or add a directional cue. Shrinking everything until it fits may preserve the picture while making it unreadable.

Avoid solving movement and camera follow at once. First make the fixed-camera room usable. Add camera movement only when it serves a larger space, then inspect whether motion hides important state or makes aiming harder.

Replace one shape with a glTF asset

Three.js provides GLTFLoader as an explicit addon for glTF 2.0 assets. Its result includes the loaded scene and any animation clips. Some compressed assets require additional decoders. Start with an uncomplicated asset so you can distinguish a loading issue from your own game logic.

Replace the mailbox shape first. Preserve the delivery zone while changing only the visible object. Check orientation, scale, pivot, and material in the actual scene lighting. If the art is twice as wide as the delivery zone, the player will receive contradictory information about where to stand.

Show a meaningful loading state and handle a failed asset request. A missing model should not leave the game silently waiting forever. Use your browser's network and console views to inspect the file request and error. Keep a simple fallback shape during development so the loop remains testable while you repair the asset.

Make completion and restart ordinary parts of the game

Restart should reset one coherent state and redraw from it. Recreating event listeners on every restart can leave multiple handlers responding to one press. Starting another animation loop without stopping the earlier one can make the simulation run twice. Reuse the existing loop or manage its lifetime explicitly.

Now you have a small game, not just a rendering exercise. If a new feature is hard to place, ask which step it belongs to: input, state change, world presentation, interface, or resource loading. That question is usually more useful than adding another library immediately.

  1. Pick up a parcel, approach the mailbox, and verify exactly one delivery.
  2. Try entering the mailbox empty and approaching a second parcel while carrying the first.
  3. Finish all three deliveries and stop accepting gameplay input that would change the completed result.
  4. Restart: reset the courier, parcels, carried state, counter, and result message.
  5. Resize, switch away and back, and test on the intended phone or computer.

Keep an assistant focused on the missing piece

The same brief works as a comparison task for coding models. Give each the same starting files and judge the resulting interaction. A model that produces a beautiful scene but loses the carried parcel on restart has not finished the task.

A brief to make your own

Inspect this Three.js project and its installed version. Keep the existing renderer and camera. Add the smallest delivery loop: one carried parcel at a time, three deliveries, visible state, and restart. Keep state separate from meshes, avoid duplicate input handlers or animation loops, and handle focus loss. Explain how to test collection, delivery, resize, and restart.

Sources & further reading

References for the tools and facts discussed here. Follow the documentation for the versions you use.

Put your idea to work with the Dashpop Game Engine. Describe a game, play it quickly, and shape it yourself. Start with one change you can test.