Build scenes visually. Add behavior with React. Ship them on the web.
React Three Game combines Three.js, React Three Fiber, and a WebGPU scene editor. A scene is a prefab: a JSON tree of nodes with components for geometry, materials, lighting, sound, and gameplay. Register your own components to give those nodes new behavior and editable properties.
npm · Website · Component playground · Starter
Open the web editor to assemble a
scene, adjust components, and try it in Play mode. The same editor exposes
window.scene for scripts and browser agents:
const scene = window.scene;
scene.help();
scene.search({ query: 'Cube' });
await scene.assets({ type: 'model', query: 'character' });
scene.update({ id: 'cube', transform: { position: [0, 2, 0] } });
scene.exportJSON();The API shares the editor's document and undo history. It supports inspecting, creating, editing, cloning, and packing nodes, plus screenshots and exports. See the Scene API guide for the full workflow. The hosted editor includes built-in components; use your own app for custom ones.
Asset pickers and scene.assets() read one /manifest.json, relative to the editor's
basePath. It contains models, textures, sound, and prefabs arrays of asset
paths. Run npm --prefix docs run generate-manifests to rebuild the demo catalog.
Run this repository's editor locally:
npm install
npm run devOpen http://localhost:3000/editor. Or use the
Starter to build your own app
with an embedded editor.
Use Node 22+ to inspect components, edit scene files, and convert between prefab JSON and GLB:
npm install -g react-three-game
rtg components
rtg schema > commands.schema.json
rtg validate scene.json commands.json
rtg apply scene.json commands.json edited.json
rtg convert scene.json scene.glb ./public
rtg convert scene.glb imported.jsonA command file uses the same operations as the web API's scene.batch():
{
"commands": [
{ "op": "transform", "id": "cube", "position": [0, 2, 0] }
]
}Run rtg --help for usage. GLB conversion exports a static scene; it does not
preserve gameplay, animation, or textures. Keep prefab JSON for playable scenes.
In a React app, install the library and its rendering dependencies:
npm install react-three-game @react-three/fiber @react-three/drei three three-textReact and React DOM are peer dependencies. Rendering needs a WebGPU-capable
browser. Physics is optional through the crashcat plugin.
A component pairs a React view with a property definition. Register it once, before mounting your editor or scene:
// Rotator.tsx
import { useFrame } from '@react-three/fiber';
import {
registerComponent, useNode, useGameObject,
type Component, type ComponentViewProps,
} from 'react-three-game/viewer';
type Props = { speed: number };
function RotatorView({ properties, children }: ComponentViewProps<Props>) {
const object = useGameObject();
const editMode = useNode(node => node.editMode);
useFrame((_, delta) => {
if (!editMode && object.transform) {
object.transform.rotation.y += properties.speed * delta;
}
});
return <>{children}</>;
}
const Rotator: Component<Props> = {
name: 'Rotator',
description: 'Rotate around Y. Speed is radians per second.',
properties: { speed: { default: 1, step: 0.1 } },
View: RotatorView,
};
registerComponent(Rotator);Rotator now appears in the editor's Add component picker, with a Speed field.
Its settings are saved in the scene; its implementation stays in your app.
Here is a small scene using it:
{
"root": {
"id": "world",
"children": [{
"id": "cube",
"name": "Cube",
"components": {
"geometry": { "type": "Geometry", "properties": { "geometryType": "box" } },
"material": { "type": "Material", "properties": { "name": "coral", "color": "#ef806a" } },
"rotator": { "type": "Rotator", "properties": { "speed": 1 } }
}
}]
}
}Save this as scene.json. Other nodes can share its material with
{ "type": "Material", "properties": { "name": "coral" } }.
Embed the editor in your app:
import './Rotator';
import { PrefabEditor } from 'react-three-game/editor';
import scene from './scene.json';
export default function App() {
return <div style={{ height: '100vh' }}>
<PrefabEditor prefab={scene}
canvasProps={{ camera: { position: [4, 3, 6] } }}>
<ambientLight intensity={2} />
</PrefabEditor>
</div>;
}Select the cube to change its speed, then press Play. Your app also gets
window.scene. For code that belongs directly in a scene file, the built-in
Runtime component offers JavaScript setup and update fields; use it only with
trusted scene files.
React Views use useEffect, useFrame, and useThree directly. Component also
provides optional setup(context) and update(context) callbacks, used by Runtime
to run authored JavaScript. They map to React effects and R3F frames, only while
enabled in Play and outside preparation.
Setup runs after committed refs/layout effects; separately loading objects may
still be absent. Return cleanup or use context.onCleanup(fn); resources are
released in reverse order on disable, exit from Play, removal or code replacement.
Setup may replay in Strict Mode. state is fresh for each setup, while properties
stays current without restarting it. Use restartOn: ['propertyName'] to opt into
restarting for selected property changes; Runtime uses this for its source fields.
Read context.three.camera, .scene, .gl, etc. for live R3F state;
context.prefab.getObject(id) resolves an object in this prefab instance.
object is this node's nullable transform, node its scoped handle, and events
the gameplay event bus (events.on returns cleanup). delta is seconds during
update, zero otherwise. Updates run at priority -1, before default-priority
callbacks and instance uploads. Setup/update errors stop updates and run cleanup.
React Views can use useSceneRuntimeContext() for the same live three/events
access or useThree(selector) for reactive reads. See the
Runtime example
for JavaScript stored directly in a scene.
Choose Save Prefab in the editor's file menu to download the scene as JSON.
You can also get the document with scene.exportJSON(), or connect
PrefabEditor's onSaveScene callback to your app's storage.
Load the saved scene with PrefabRoot inside GameCanvas to run it without
editor panels:
import './Rotator';
import { GameCanvas, PrefabRoot } from 'react-three-game/viewer';
import scene from './scene.json';
export default function App() {
return <div style={{ height: '100vh' }}>
<GameCanvas camera={{ position: [4, 3, 6] }}>
<ambientLight intensity={2} />
<PrefabRoot data={scene} />
</GameCanvas>
</div>;
}Ship the scene JSON, referenced assets, and your custom component code with the
app. Register the same components in both editor and playback builds. In Next.js,
mark the component containing the canvas with 'use client'.
| What you want to build | Start here |
|---|---|
| Load and place prefabs by URL | Loading pattern |
| Connect gameplay to scene objects | Gameplay example |
| First-person parkour and character imports | Parkour demo |
| Physics | Cool stuff demo |
| Custom inspector controls | Inspector example |
| Architecture and runtime details | Implementation map |
| More working scenes | Demo index |
npm run dev # Run the local website and editor
npm test # Library and demo tests
npm run test:package # Check the published package and CLI
npm run build # Build the library
npm --prefix docs run build # Build the websiteLicense: see LICENSE.