Three.js
Build with @remix-gg/three, the wrapper that owns the renderer, viewport, loop, input and the platform handshake.
@remix-gg/three is a thin layer over Three.js for games on Remix. One call, createGame, creates the renderer with the right settings, sizes the viewport for the design box, runs a fixed-step loop, wires input, loads the asset manifest and completes the platform handshake before your setup runs. A game on the wrapper never constructs a WebGLRenderer itself.
The engine is not inlined into your build. Published games import a pinned browser engine by URL, so a published document stays small.
New projects from Remix Desktop or remix create game start from plain Three.js and a small remix.ts that connects the game to the host, without the wrapper. Games built on @remix-gg/three keep working, and this page covers them.
The Shape of a Game
import { createGame, portraitRig } from '@remix-gg/three'
import * as THREE from '@remix-gg/three/three'
const game = await createGame({
design: { width: 720, height: 1080 },
camera: portraitRig(),
assets: {
models: { hero: 'assets/hero.glb' },
textures: { ground: 'assets/ground.png' },
audio: { hit: 'assets/hit.mp3' },
},
setup(game) {
const hero = game.assets.model('hero')
game.scene.add(hero)
// build everything once
},
restart(game, data) {
// reset run state; honour data?.levelIndex in level games
},
update(game, step) {
// fixed step in seconds; simulation only
},
render(game, dt, alpha) {
// per frame; interpolate with alpha
},
})
createGame resolves with a RemixGame:
| Member | What it is |
|---|---|
renderer, scene, camera, rig | The Three.js objects and the camera rig you chose. |
viewport | Design box, real size, pixel ratio and safeRect, the area guaranteed visible and outside the device's notch and home indicator. |
input | Pointer input in design units, with raycast helpers against the scene. |
assets | The loaded manifest: model(), texture(), sound(). |
audio | A bus that already honours the host's mute. |
hud | DOM overlay anchored inside the safe rect. |
platform | The Remix SDK, already connected. platform.info, platform.player, platform.leaderboards, platform.saves, platform.achievements, platform.realtime. |
transient | A disposal scope cleared on every restart. Register anything created after setup. |
gameOver(score, levelAttempt?) | End the run. Latched to one call per run. |
restart(data?), pause(), resume(), dispose() | Lifecycle controls. |
The Ten Rules
- Import Three.js from
'three'or'@remix-gg/three/three', which load the same pinned engine, and addons from'three/addons/…'. A game installs nothing, so never add apackage.json. - All setup goes through
createGame. Never construct aWebGLRenderer. - Reference assets with literal strings such as
'assets/hero.glb'. Publish finds them by scanning the built code; a path built at runtime is not hosted. - Call
game.gameOver(score, levelAttempt?)exactly once per run. Level games pass{ levelIndex, stars }. - Implement
restart. Play-again is already wired to it, it will be called many times in one session, and it must reset state rather than reload the page. Keepdata.levelIndexintact. - Design at the design box, but read
game.viewport.safeRectfor anything the player must see or tap. Backgrounds may bleed. - Pointer only on mobile targets. No keyboard, no hover.
- Register anything created after
setupingame.transient. Never dispose anything ingame.assets. - Budget: at most 150 draw calls and 150k triangles. Use pooling and instanced geometry.
- Pick a rig once in
createGameand usefitBoundsinstead of hand-tuned camera coordinates.
The Platform Through the Wrapper
game.platform wraps the surface documented in the SDK pages, with the handshake, the one-gameOver-per-run latch and the play-again wiring already handled. Reach leaderboards, saves and achievements as game.platform.leaderboards, game.platform.saves and game.platform.achievements, haptics as game.platform.haptic('light'), and purchases as game.platform.hasItem(slug), game.platform.itemCount(slug) and game.platform.purchase(slug).
Desktop Targets
A desktop game declares presentation.target: "desktop" in remix.json and composes for a landscape 16:9 frame: pass both design: LANDSCAPE_DESIGN (1280 by 720) and camera: landscapeRig() to createGame, because the defaults stay portrait. Read the keyboard with ordinary DOM key events; for mouse look, call game.input.lockPointer() from a press and read the look event. The viewport is any size and changes when the window does, so read it when you lay out, never once at boot.
Types in the Editor
The scaffolded project carries remix-three.d.ts, a small entry point for the generated declarations under .remix/types/, so TypeScript understands Three.js and the SDK without an install. Outside a scaffolded project, the @remix-gg/three npm package ships the same types.