Skip to content

About

Batteries included game engine for react-three-fiber.

Topics

Resources

Stars

28 stars

Watchers

0 watching

Forks

Repository files navigation

React Three Game

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

Three ways to work

Web editor and Scene API

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.

Local editor

Run this repository's editor locally:

npm install
npm run dev

Open http://localhost:3000/editor. Or use the Starter to build your own app with an embedded editor.

Node CLI

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.json

A 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.

Create custom components

In a React app, install the library and its rendering dependencies:

npm install react-three-game @react-three/fiber @react-three/drei three three-text

React 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.

Component callbacks and context

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.

Export and play your 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'.

Explore more

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

Development

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 website

License: see LICENSE.

About

Batteries included game engine for react-three-fiber.

Topics

Resources

Stars

28 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages