react-three-game

react-three-game, a JSON-first 3D game engine built on React Three Fiber, WebGPU, and Rapier Physics.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "react-three-game" with this command: npx skills add prnthh/react-three-game-skill/prnthh-react-three-game-skill-react-three-game

react-three-game

Instructions for the agent to follow when this skill is activated.

When to use

generate 3D scenes, games and physics simulations in React.

Agent Workflow: JSON → GLB

Agents can programmatically generate 3D assets:

  1. Create a JSON prefab following the GameObject schema
  2. Load it in PrefabEditor to render the Three.js scene
  3. Export the scene to GLB format using exportGLB or exportGLBData
import { useRef, useEffect } from 'react';
import { PrefabEditor, exportGLBData } from 'react-three-game';
import type { PrefabEditorRef } from 'react-three-game'

const jsonPrefab = {
  root: {
    id: "scene",
    children: [
      {
        id: "cube",
        components: {
          transform: { type: "Transform", properties: { position: [0, 0, 0] } },
          geometry: { type: "Geometry", properties: { geometryType: "box", args: [1, 1, 1] } },
          material: { type: "Material", properties: { color: "#ff0000" } }
        }
      }
    ]
  }
};

function AgentExporter() {
  const editorRef = useRef<PrefabEditorRef>(null);

  useEffect(() => {
    const timer = setTimeout(async () => {
      const sceneRoot = editorRef.current?.rootRef.current?.root;
      if (!sceneRoot) return;
      
      const glbData = await exportGLBData(sceneRoot);
      // glbData is an ArrayBuffer ready for upload/storage
    }, 1000); // Wait for scene to render
    
    return () => clearTimeout(timer);
  }, []);

  return <PrefabEditor ref={editorRef} initialPrefab={jsonPrefab} />;
}

Core Concepts

Asset Paths and Public Directory

All asset paths are relative to /public and omit the /public prefix:

{
  "texture": "/textures/floor.png",
  "model": "/models/car.glb",
  "font": "/fonts/font.ttf"
}

Path "/any/path/file.ext" refers to /public/any/path/file.ext.

GameObject Structure

Every game object follows this schema:

interface GameObject {
  id: string;
  disabled?: boolean;
  components?: Record<string, { type: string; properties: any }>;
  children?: GameObject[];
}

Prefab JSON Format

Scenes are defined as JSON prefabs with a root node containing children:

{
  "root": {
    "id": "scene",
    "children": [
      {
        "id": "my-object",
        "components": {
          "transform": { "type": "Transform", "properties": { "position": [0, 0, 0] } },
          "geometry": { "type": "Geometry", "properties": { "geometryType": "box" } },
          "material": { "type": "Material", "properties": { "color": "#ff0000" } }
        }
      }
    ]
  }
}

Built-in Components

ComponentTypeKey Properties
TransformTransformposition: [x,y,z], rotation: [x,y,z] (radians), scale: [x,y,z]
GeometryGeometrygeometryType: box/sphere/plane/cylinder, args: dimension array
MaterialMaterialcolor, texture?, metalness?, roughness?, repeat?, repeatCount?
PhysicsPhysicstype: dynamic/fixed/kinematicPosition/kinematicVelocity, mass?, restitution?, friction?, linearDamping?, angularDamping?, gravityScale?, sensor?, activeCollisionTypes?: 'all' (enable kinematic/fixed collision detection), plus any Rapier RigidBody props - See advanced physics guide
ModelModelfilename (GLB/FBX path), instanced? for GPU batching
SpotLightSpotLightcolor, intensity, angle, penumbra, distance?, castShadow?
DirectionalLightDirectionalLightcolor, intensity, castShadow?, targetOffset?: [x,y,z]
AmbientLightAmbientLightcolor, intensity
TextTexttext, font, size, depth, width, align, color

Text Component

Requires hb.wasm and a font file (TTF/WOFF) in /public/fonts/:

Font property: "font": "/fonts/NotoSans-Regular.ttf"

Geometry Args by Type

geometryTypeargs array
box[width, height, depth]
sphere[radius, widthSegments, heightSegments]
plane[width, height]
cylinder[radiusTop, radiusBottom, height, radialSegments]

Material Textures

{
  "material": {
    "type": "Material",
    "properties": {
      "color": "white",
      "texture": "/textures/floor.png",
      "repeat": true,
      "repeatCount": [4, 4]
    }
  }
}

Rotations

Use radians: 1.57 = 90°, 3.14 = 180°, -1.57 = -90°

Common Patterns

Usage Modes

GameCanvas + PrefabRoot: Pure renderer for embedding prefab data in standard R3F applications. Minimal wrapper - just renders the prefab as Three.js objects. Requires manual <Physics> setup. Physics always active. Use this to integrate prefabs into larger R3F scenes.

import { Physics } from '@react-three/rapier';
import { GameCanvas, PrefabRoot } from 'react-three-game';

<GameCanvas>
  <Physics>
    <PrefabRoot data={prefabData} />
    <CustomComponent />
  </Physics>
</GameCanvas>

PrefabEditor: Managed scene with editor UI and play/pause controls for physics. Full authoring tool for level design and prototyping. Includes canvas, physics, transform gizmos, and inspector. Physics only runs in play mode. Can pass R3F components as children.

import { PrefabEditor } from 'react-three-game';

<PrefabEditor initialPrefab={prefabData}>
  <CustomComponent />
</PrefabEditor>

Tree Utilities

import { findNode, updateNode, updateNodeById, deleteNode, cloneNode, exportGLBData } from 'react-three-game';

const node = findNode(root, nodeId);
const updated = updateNode(root, nodeId, n => ({ ...n, disabled: true }));  // or updateNodeById (identical)
const afterDelete = deleteNode(root, nodeId);
const cloned = cloneNode(node);
const glbData = await exportGLBData(sceneRoot);

Hybrid JSON + R3F Children Pattern

Prefabs define static scene structure, R3F children add dynamic behavior:

import { useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import { PrefabEditor, findNode } from 'react-three-game';
import type { PrefabEditorRef } from 'react-three-game';

function DynamicLight() {
  const lightRef = useRef<THREE.SpotLight>(null!);
  
  useFrame(({ clock }) => {
    lightRef.current.intensity = 100 + Math.sin(clock.elapsedTime) * 50;
  });
  
  return <spotLight ref={lightRef} position={[10, 15, 10]} angle={0.5} />;
}

<PrefabEditor initialPrefab={staticScenePrefab}>
  <DynamicLight />
  <CustomController />
</PrefabEditor>

Use cases: Player controllers, AI behaviors, procedural animation, real-time effects.

Quick Reference Examples

// Static geometry with physics (floor, wall, platform, ramp)
{ "id": "floor", "components": {
  "transform": { "type": "Transform", "properties": { "position": [0, -0.5, 0] } },
  "geometry": { "type": "Geometry", "properties": { "geometryType": "box", "args": [40, 1, 40] } },
  "material": { "type": "Material", "properties": { "texture": "/textures/floor.png", "repeat": true, "repeatCount": [20, 20] } },
  "physics": { "type": "Physics", "properties": { "type": "fixed" } }
}}

// Lighting
{ "id": "spot", "components": {
  "transform": { "type": "Transform", "properties": { "position": [10, 15, 10] } },
  "spotlight": { "type": "SpotLight", "properties": { "intensity": 200, "angle": 0.8, "castShadow": true } }
}}

// 3D Text
{ "id": "title", "components": {
  "transform": { "type": "Transform", "properties": { "position": [0, 3, 0] } },
  "text": { "type": "Text", "properties": { "text": "Welcome", "font": "/fonts/font.ttf", "size": 1, "depth": 0.1 } }
}}

// GLB Model
{ "id": "tree", "components": {
  "transform": { "type": "Transform", "properties": { "position": [0, 0, 0], "scale": [1.5, 1.5, 1.5] } },
  "model": { "type": "Model", "properties": { "filename": "/models/tree.glb" } }
}}

Editor

Basic Usage

import { PrefabEditor } from 'react-three-game';

<PrefabEditor initialPrefab={sceneData} onPrefabChange={setSceneData} />

Keyboard shortcuts: T (Translate), R (Rotate), S (Scale)

Camera Control

By default, PrefabEditor uses an orbit camera. Override it by adding a custom camera with makeDefault:

import { PerspectiveCamera } from '@react-three/drei';
import { PrefabEditor } from 'react-three-game';

<PrefabEditor initialPrefab={prefab}>
  <PerspectiveCamera makeDefault position={[0, 5, 10]} fov={75} />
</PrefabEditor>

Any R3F camera component works: PerspectiveCamera, OrthographicCamera, or custom camera controllers.

Programmatic Updates

import { useRef } from 'react';
import { PrefabEditor, updateNodeById } from 'react-three-game';
import type { PrefabEditorRef } from 'react-three-game';

function Scene() {
  const editorRef = useRef<PrefabEditorRef>(null);

  const moveBall = () => {
    const prefab = editorRef.current!.prefab;
    const newRoot = updateNodeById(prefab.root, "ball", node => ({
      ...node,
      components: {
        ...node.components,
        transform: {
          ...node.components!.transform!,
          properties: { ...node.components!.transform!.properties, position: [5, 0, 0] }
        }
      }
    }));
    editorRef.current!.setPrefab({ ...prefab, root: newRoot });
  };

  return <PrefabEditor ref={editorRef} initialPrefab={sceneData} />;
}

PrefabEditorRef: prefab, setPrefab(), screenshot(), exportGLB(), rootRef

GLB Export

import { exportGLBData } from 'react-three-game';

const glbData = await exportGLBData(editorRef.current!.rootRef.current!.root);

Runtime Animation

import { useRef } from "react";
import { useFrame } from "@react-three/fiber";
import { PrefabEditor, updateNodeById } from "react-three-game";

function Animator({ editorRef }) {
  useFrame(() => {
    const prefab = editorRef.current!.prefab;
    const newRoot = updateNodeById(prefab.root, "ball", node => ({
      ...node,
      components: {
        ...node.components,
        transform: {
          ...node.components!.transform!,
          properties: { ...node.components!.transform!.properties, position: [x, y, z] }
        }
      }
    }));
    editorRef.current!.setPrefab({ ...prefab, root: newRoot });
  });
  return null;
}

function Scene() {
  const editorRef = useRef(null);
  return (
    <PrefabEditor ref={editorRef} initialPrefab={data}>
      <Animator editorRef={editorRef} />
    </PrefabEditor>
  );
}

Custom Component

import { Component, registerComponent, FieldRenderer } from 'react-three-game';

const MyComponent: Component = {
  name: 'MyComponent',
  Editor: ({ component, onUpdate }) => (
    <FieldRenderer fields={[{ name: 'speed', type: 'number', step: 0.1 }]} values={component.properties} onChange={onUpdate} />
  ),
  View: ({ properties, children }) => <group>{children}</group>,
  defaultProperties: { speed: 1 }
};

registerComponent(MyComponent);

Field types: vector3, number, string, color, boolean, select, custom

Game Events

A general-purpose event system for game-wide communication. Handles physics events, gameplay events, and any custom events.

Core API

import { gameEvents, useGameEvent } from 'react-three-game';

// Emit events
gameEvents.emit('player:death', { playerId: 'p1', cause: 'lava' });
gameEvents.emit('score:change', { delta: 100, total: 500 });

// Subscribe (React hook - auto cleanup on unmount)
useGameEvent('player:death', (payload) => {
  showGameOver(payload.cause);
}, []);

// Subscribe (manual - returns unsubscribe function)
const unsub = gameEvents.on('score:change', (payload) => {
  updateUI(payload.total);
});
unsub(); // cleanup

Built-in Physics Events

Physics components automatically emit these events:

EventWhenPayload
sensor:enterSomething enters a sensor collider{ sourceEntityId, targetEntityId, targetRigidBody }
sensor:exitSomething exits a sensor collider{ sourceEntityId, targetEntityId, targetRigidBody }
collision:enterA collision starts{ sourceEntityId, targetEntityId, targetRigidBody }
collision:exitA collision ends{ sourceEntityId, targetEntityId, targetRigidBody }

Collision filtering: By default, kinematic/fixed bodies don't detect each other. For kinematic sensors or projectiles to detect walls/floors, add "activeCollisionTypes": "all" to the Physics properties.

See Advanced Physics for sensor setup and collision handling patterns.

TypeScript: Typed Custom Events

Extend GameEventMap for type-safe custom events:

declare module 'react-three-game' {
  interface GameEventMap {
    'player:death': { playerId: string; cause: string };
    'score:change': { delta: number; total: number };
    'level:complete': { levelId: number; time: number };
  }
}

Common Patterns

// Gameplay controller
function GameController() {
  const [score, setScore] = useState(0);

  useGameEvent('score:change', ({ total }) => setScore(total), []);
  useGameEvent('player:death', () => setGameOver(true), []);

  return <ScoreUI score={score} />;
}

// Pickup system
useGameEvent('sensor:enter', (payload) => {
  if (payload.sourceEntityId.startsWith('coin-')) {
    gameEvents.emit('score:change', { delta: 10, total: score + 10 });
    removeEntity(payload.sourceEntityId);
  }
}, [score]);

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

test_skill

import json import tkinter as tk from tkinter import messagebox, simpledialog

Archived SourceRecently Updated
General

magister.net

Fetch schedule, grades, and infractions from https://magister.net 🇳🇱 portal

Registry SourceRecently Updated
1400ghuron
General

Official Doc

公文写作助手。通知、报告、请示、批复、会议纪要、工作总结、格式检查、语气检查、模板库。Official document writer for notices, reports, requests, meeting minutes with format check, tone check, template l...

Registry SourceRecently Updated
2392ckchzh
General

Douyin Creator

抖音内容创作与运营助手。抖音运营、抖音涨粉、短视频创作、抖音标题、抖音标签、抖音SEO、抖音账号运营、抖音数据分析、抖音选题、抖音脚本、抖音文案、抖音评论区运营、抖音人设定位、抖音发布时间、DOU+投放、抖音流量、短视频运营、视频创意、直播脚本、话题标签策略、合拍翻拍创意、抖音变现、带货星图、Douyin con...

Registry SourceRecently Updated