소스 검색

react-three-fiber (#209)

Chris Tate 4 달 전
부모
커밋
f6d1c5134b
39개의 변경된 파일9447개의 추가작업 그리고 45개의 파일을 삭제
  1. 2 1
      .changeset/config.json
  2. 42 0
      README.md
  3. 416 0
      apps/web/app/(main)/docs/api/react-three-fiber/page.mdx
  4. 2 2
      apps/web/app/api/docs-chat/route.ts
  5. 4 0
      apps/web/lib/docs-navigation.ts
  6. 1 0
      apps/web/lib/page-titles.ts
  7. 20 0
      examples/react-three-fiber/app/layout.tsx
  8. 238 0
      examples/react-three-fiber/app/page.tsx
  9. 9 0
      examples/react-three-fiber/app/scenes/_helpers.ts
  10. 474 0
      examples/react-three-fiber/app/scenes/clockwork-orrery.ts
  11. 692 0
      examples/react-three-fiber/app/scenes/deep-sea-abyss.ts
  12. 940 0
      examples/react-three-fiber/app/scenes/floating-islands.ts
  13. 164 0
      examples/react-three-fiber/app/scenes/hyperspace-tunnel.ts
  14. 29 0
      examples/react-three-fiber/app/scenes/index.ts
  15. 400 0
      examples/react-three-fiber/app/scenes/mystify.ts
  16. 554 0
      examples/react-three-fiber/app/scenes/orbital-chaos.ts
  17. 307 0
      examples/react-three-fiber/app/scenes/perpetual-motion.ts
  18. 764 0
      examples/react-three-fiber/app/scenes/pipes.ts
  19. 809 0
      examples/react-three-fiber/app/scenes/portal-gallery.ts
  20. 455 0
      examples/react-three-fiber/app/scenes/product-showroom.ts
  21. 149 0
      examples/react-three-fiber/app/scenes/starfield.ts
  22. 295 0
      examples/react-three-fiber/app/scenes/storm-cell.ts
  23. 12 0
      examples/react-three-fiber/eslint.config.js
  24. 6 0
      examples/react-three-fiber/next-env.d.ts
  25. 12 0
      examples/react-three-fiber/next.config.ts
  26. 35 0
      examples/react-three-fiber/package.json
  27. 13 0
      examples/react-three-fiber/tsconfig.json
  28. 75 0
      packages/react-three-fiber/README.md
  29. 80 0
      packages/react-three-fiber/package.json
  30. 841 0
      packages/react-three-fiber/src/catalog.ts
  31. 909 0
      packages/react-three-fiber/src/components.tsx
  32. 25 0
      packages/react-three-fiber/src/index.ts
  33. 19 0
      packages/react-three-fiber/src/r3f-jsx.d.ts
  34. 144 0
      packages/react-three-fiber/src/renderer.tsx
  35. 25 0
      packages/react-three-fiber/src/schemas.ts
  36. 9 0
      packages/react-three-fiber/tsconfig.json
  37. 19 0
      packages/react-three-fiber/tsup.config.ts
  38. 277 42
      pnpm-lock.yaml
  39. 180 0
      skills/react-three-fiber/SKILL.md

+ 2 - 1
.changeset/config.json

@@ -20,7 +20,8 @@
       "@json-render/image",
       "@json-render/mcp",
       "@json-render/svelte",
-      "@json-render/solid"
+      "@json-render/solid",
+      "@json-render/react-three-fiber"
     ]
   ],
   "linked": [],

+ 42 - 0
README.md

@@ -121,6 +121,7 @@ function Dashboard({ spec }) {
 | `@json-render/svelte`       | Svelte 5 renderer with runes-based reactivity                          |
 | `@json-render/solid`        | SolidJS renderer with fine-grained reactive contexts                   |
 | `@json-render/shadcn`       | 36 pre-built shadcn/ui components (Radix UI + Tailwind CSS)            |
+| `@json-render/react-three-fiber` | React Three Fiber renderer for 3D scenes (19 built-in components)  |
 | `@json-render/react-native` | React Native renderer with standard mobile components                  |
 | `@json-render/remotion`     | Remotion video renderer, timeline schema                               |
 | `@json-render/react-pdf`    | React PDF renderer for generating PDF documents from specs             |
@@ -434,6 +435,47 @@ import { renderToSvg } from "@json-render/image/render";
 const svg = await renderToSvg(spec, { fonts });
 ```
 
+### Three.js (3D)
+
+```tsx
+import { defineCatalog } from "@json-render/core";
+import { schema, defineRegistry } from "@json-render/react";
+import {
+  threeComponentDefinitions,
+  threeComponents,
+  ThreeCanvas,
+} from "@json-render/react-three-fiber";
+
+const catalog = defineCatalog(schema, {
+  components: {
+    Box: threeComponentDefinitions.Box,
+    Sphere: threeComponentDefinitions.Sphere,
+    AmbientLight: threeComponentDefinitions.AmbientLight,
+    DirectionalLight: threeComponentDefinitions.DirectionalLight,
+    OrbitControls: threeComponentDefinitions.OrbitControls,
+  },
+  actions: {},
+});
+
+const { registry } = defineRegistry(catalog, {
+  components: {
+    Box: threeComponents.Box,
+    Sphere: threeComponents.Sphere,
+    AmbientLight: threeComponents.AmbientLight,
+    DirectionalLight: threeComponents.DirectionalLight,
+    OrbitControls: threeComponents.OrbitControls,
+  },
+});
+
+<ThreeCanvas
+  spec={spec}
+  registry={registry}
+  shadows
+  camera={{ position: [5, 5, 5], fov: 50 }}
+  style={{ width: "100%", height: "100vh" }}
+/>;
+```
+
 ## Features
 
 ### Streaming (SpecStream)

+ 416 - 0
apps/web/app/(main)/docs/api/react-three-fiber/page.mdx

@@ -0,0 +1,416 @@
+import { pageMetadata } from "@/lib/page-metadata"
+export const metadata = pageMetadata("docs/api/react-three-fiber")
+
+# @json-render/react-three-fiber
+
+React Three Fiber renderer for json-render. 19 built-in 3D components for meshes, lights, models, environments, text, cameras, and controls.
+
+## Installation
+
+```bash
+npm install @json-render/react-three-fiber @json-render/core @json-render/react @react-three/fiber @react-three/drei three zod
+```
+
+## Entry Points
+
+<table>
+<thead>
+<tr>
+<th>Entry Point</th>
+<th>Exports</th>
+<th>Use For</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>@json-render/react-three-fiber</code></td>
+<td><code>threeComponents</code>, <code>ThreeRenderer</code>, <code>ThreeCanvas</code>, schemas</td>
+<td>React Three Fiber implementations and renderer</td>
+</tr>
+<tr>
+<td><code>@json-render/react-three-fiber/catalog</code></td>
+<td><code>threeComponentDefinitions</code></td>
+<td>Catalog schemas (no R3F dependency, safe for server)</td>
+</tr>
+</tbody>
+</table>
+
+## Usage
+
+```tsx
+import {'{ defineCatalog }'} from "@json-render/core";
+import {'{ schema, defineRegistry }'} from "@json-render/react";
+import {'{'}
+  threeComponentDefinitions,
+  threeComponents,
+  ThreeCanvas,
+{'}'} from "@json-render/react-three-fiber";
+
+const catalog = defineCatalog(schema, {'{'}
+  components: {'{'}
+    Box: threeComponentDefinitions.Box,
+    Sphere: threeComponentDefinitions.Sphere,
+    AmbientLight: threeComponentDefinitions.AmbientLight,
+    DirectionalLight: threeComponentDefinitions.DirectionalLight,
+    OrbitControls: threeComponentDefinitions.OrbitControls,
+  {'}'},
+  actions: {'{}'},
+{'}'});
+
+const {'{ registry }'} = defineRegistry(catalog, {'{'}
+  components: {'{'}
+    Box: threeComponents.Box,
+    Sphere: threeComponents.Sphere,
+    AmbientLight: threeComponents.AmbientLight,
+    DirectionalLight: threeComponents.DirectionalLight,
+    OrbitControls: threeComponents.OrbitControls,
+  {'}'},
+{'}'});
+```
+
+### ThreeCanvas (convenience)
+
+```tsx
+<ThreeCanvas
+  spec={'{spec}'}
+  registry={'{registry}'}
+  shadows
+  camera={'{'}{'{ position: [5, 5, 5], fov: 50 }'}{'}'} 
+  style={'{'}{'{ width: "100%", height: "100vh" }'}{'}'} 
+/>
+```
+
+### Manual Canvas Setup
+
+```tsx
+import {'{ Canvas }'} from "@react-three/fiber";
+import {'{ ThreeRenderer }'} from "@json-render/react-three-fiber";
+
+<Canvas shadows>
+  <ThreeRenderer spec={'{spec}'} registry={'{registry}'} />
+</Canvas>
+```
+
+## Components
+
+### Primitives
+
+<table>
+<thead>
+<tr>
+<th>Component</th>
+<th>Description</th>
+<th>Key Props</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>Box</code></td>
+<td>Box mesh (default 1x1x1)</td>
+<td><code>width</code>, <code>height</code>, <code>depth</code>, <code>material</code></td>
+</tr>
+<tr>
+<td><code>Sphere</code></td>
+<td>Sphere mesh</td>
+<td><code>radius</code>, <code>widthSegments</code>, <code>heightSegments</code>, <code>material</code></td>
+</tr>
+<tr>
+<td><code>Cylinder</code></td>
+<td>Cylinder mesh</td>
+<td><code>radiusTop</code>, <code>radiusBottom</code>, <code>height</code>, <code>material</code></td>
+</tr>
+<tr>
+<td><code>Cone</code></td>
+<td>Cone mesh</td>
+<td><code>radius</code>, <code>height</code>, <code>material</code></td>
+</tr>
+<tr>
+<td><code>Torus</code></td>
+<td>Torus (donut) mesh</td>
+<td><code>radius</code>, <code>tube</code>, <code>material</code></td>
+</tr>
+<tr>
+<td><code>Plane</code></td>
+<td>Flat plane mesh</td>
+<td><code>width</code>, <code>height</code>, <code>material</code></td>
+</tr>
+<tr>
+<td><code>Capsule</code></td>
+<td>Capsule mesh</td>
+<td><code>radius</code>, <code>length</code>, <code>material</code></td>
+</tr>
+</tbody>
+</table>
+
+All primitives share: <code>position</code>, <code>rotation</code>, <code>scale</code>, <code>castShadow</code>, <code>receiveShadow</code>, <code>material</code>.
+
+### Material Schema
+
+<table>
+<thead>
+<tr>
+<th>Property</th>
+<th>Type</th>
+<th>Default</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>color</code></td>
+<td><code>string</code></td>
+<td><code>"#ffffff"</code></td>
+</tr>
+<tr>
+<td><code>metalness</code></td>
+<td><code>number</code></td>
+<td><code>0</code></td>
+</tr>
+<tr>
+<td><code>roughness</code></td>
+<td><code>number</code></td>
+<td><code>1</code></td>
+</tr>
+<tr>
+<td><code>emissive</code></td>
+<td><code>string</code></td>
+<td><code>"#000000"</code></td>
+</tr>
+<tr>
+<td><code>emissiveIntensity</code></td>
+<td><code>number</code></td>
+<td><code>1</code></td>
+</tr>
+<tr>
+<td><code>opacity</code></td>
+<td><code>number</code></td>
+<td><code>1</code></td>
+</tr>
+<tr>
+<td><code>transparent</code></td>
+<td><code>boolean</code></td>
+<td><code>false</code></td>
+</tr>
+<tr>
+<td><code>wireframe</code></td>
+<td><code>boolean</code></td>
+<td><code>false</code></td>
+</tr>
+</tbody>
+</table>
+
+### Lights
+
+<table>
+<thead>
+<tr>
+<th>Component</th>
+<th>Description</th>
+<th>Key Props</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>AmbientLight</code></td>
+<td>Uniform illumination</td>
+<td><code>color</code>, <code>intensity</code></td>
+</tr>
+<tr>
+<td><code>DirectionalLight</code></td>
+<td>Sunlight-style</td>
+<td><code>position</code>, <code>color</code>, <code>intensity</code>, <code>castShadow</code></td>
+</tr>
+<tr>
+<td><code>PointLight</code></td>
+<td>Radiates from a point</td>
+<td><code>position</code>, <code>color</code>, <code>intensity</code>, <code>distance</code>, <code>decay</code></td>
+</tr>
+<tr>
+<td><code>SpotLight</code></td>
+<td>Cone of light</td>
+<td><code>position</code>, <code>color</code>, <code>intensity</code>, <code>angle</code>, <code>penumbra</code></td>
+</tr>
+</tbody>
+</table>
+
+### Other Components
+
+<table>
+<thead>
+<tr>
+<th>Component</th>
+<th>Description</th>
+<th>Key Props</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>Group</code></td>
+<td>Container for children</td>
+<td><code>position</code>, <code>rotation</code>, <code>scale</code></td>
+</tr>
+<tr>
+<td><code>Model</code></td>
+<td>GLTF/GLB model loader</td>
+<td><code>url</code>, <code>position</code>, <code>rotation</code>, <code>scale</code></td>
+</tr>
+<tr>
+<td><code>Environment</code></td>
+<td>HDRI environment map</td>
+<td><code>preset</code>, <code>background</code>, <code>blur</code>, <code>intensity</code></td>
+</tr>
+<tr>
+<td><code>Fog</code></td>
+<td>Linear fog effect</td>
+<td><code>color</code>, <code>near</code>, <code>far</code></td>
+</tr>
+<tr>
+<td><code>GridHelper</code></td>
+<td>Reference grid</td>
+<td><code>size</code>, <code>divisions</code>, <code>color</code></td>
+</tr>
+<tr>
+<td><code>Text3D</code></td>
+<td>3D text (SDF)</td>
+<td><code>text</code>, <code>fontSize</code>, <code>color</code>, <code>anchorX</code>, <code>anchorY</code></td>
+</tr>
+<tr>
+<td><code>PerspectiveCamera</code></td>
+<td>Camera</td>
+<td><code>position</code>, <code>fov</code>, <code>near</code>, <code>far</code>, <code>makeDefault</code></td>
+</tr>
+<tr>
+<td><code>OrbitControls</code></td>
+<td>Camera controls</td>
+<td><code>enableDamping</code>, <code>enableZoom</code>, <code>autoRotate</code></td>
+</tr>
+</tbody>
+</table>
+
+## Shared Schemas
+
+Reusable Zod schemas for custom 3D components:
+
+```tsx
+import {'{ vector3Schema, materialSchema, transformProps, shadowProps }'} from "@json-render/react-three-fiber";
+```
+
+<table>
+<thead>
+<tr>
+<th>Export</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>vector3Schema</code></td>
+<td><code>z.tuple([z.number(), z.number(), z.number()])</code></td>
+</tr>
+<tr>
+<td><code>materialSchema</code></td>
+<td>Standard material props (color, metalness, roughness, etc.)</td>
+</tr>
+<tr>
+<td><code>transformProps</code></td>
+<td><code>{'{ position, rotation, scale }'}</code> schema fields</td>
+</tr>
+<tr>
+<td><code>shadowProps</code></td>
+<td><code>{'{ castShadow, receiveShadow }'}</code> schema fields</td>
+</tr>
+</tbody>
+</table>
+
+## ThreeRenderer
+
+<table>
+<thead>
+<tr>
+<th>Prop</th>
+<th>Type</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>spec</code></td>
+<td><code>Spec | null</code></td>
+<td>The spec to render as a 3D scene</td>
+</tr>
+<tr>
+<td><code>registry</code></td>
+<td><code>ComponentRegistry</code></td>
+<td>Component registry from <code>defineRegistry</code></td>
+</tr>
+<tr>
+<td><code>store</code></td>
+<td><code>StateStore</code></td>
+<td>External state store (controlled mode)</td>
+</tr>
+<tr>
+<td><code>initialState</code></td>
+<td><code>Record&lt;string, unknown&gt;</code></td>
+<td>Initial state (uncontrolled mode)</td>
+</tr>
+<tr>
+<td><code>handlers</code></td>
+<td><code>Record&lt;string, Function&gt;</code></td>
+<td>Action handlers</td>
+</tr>
+<tr>
+<td><code>loading</code></td>
+<td><code>boolean</code></td>
+<td>Whether the spec is streaming</td>
+</tr>
+<tr>
+<td><code>children</code></td>
+<td><code>ReactNode</code></td>
+<td>Additional R3F elements alongside the spec</td>
+</tr>
+</tbody>
+</table>
+
+## ThreeCanvas
+
+Extends <code>ThreeRendererProps</code> with Canvas options:
+
+<table>
+<thead>
+<tr>
+<th>Prop</th>
+<th>Type</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>shadows</code></td>
+<td><code>boolean</code></td>
+<td>Enable shadow maps</td>
+</tr>
+<tr>
+<td><code>camera</code></td>
+<td><code>object</code></td>
+<td>Default camera config (position, fov, etc.)</td>
+</tr>
+<tr>
+<td><code>className</code></td>
+<td><code>string</code></td>
+<td>CSS class for the canvas container</td>
+</tr>
+<tr>
+<td><code>style</code></td>
+<td><code>CSSProperties</code></td>
+<td>Inline styles for the canvas container</td>
+</tr>
+</tbody>
+</table>
+
+## Type Helpers
+
+```tsx
+import type {'{ ThreeProps }'} from "@json-render/react-three-fiber";
+
+type BoxProps = ThreeProps<"Box">;
+type SphereProps = ThreeProps<"Sphere">;
+```

+ 2 - 2
apps/web/app/api/docs-chat/route.ts

@@ -16,8 +16,8 @@ const SYSTEM_PROMPT = `You are a helpful documentation assistant for json-render
 
 GitHub repository: https://github.com/vercel-labs/json-render
 Documentation: https://json-render.dev/docs
-npm packages: @json-render/core, @json-render/react, @json-render/vue, @json-render/svelte, @json-render/solid, @json-render/shadcn, @json-render/react-native, @json-render/react-email, @json-render/react-pdf, @json-render/image, @json-render/remotion, @json-render/codegen, @json-render/mcp, @json-render/redux, @json-render/zustand, @json-render/jotai, @json-render/xstate
-Skills: json-render ships AI agent skills that teach coding agents how to use each package. Install with "npx skills add vercel-labs/json-render --skill <name>". Available skills: core, react, react-pdf, react-email, react-native, shadcn, image, remotion, vue, svelte, solid, codegen, mcp, redux, zustand, jotai, xstate. See /docs/skills for details.
+npm packages: @json-render/core, @json-render/react, @json-render/vue, @json-render/svelte, @json-render/solid, @json-render/shadcn, @json-render/react-three-fiber, @json-render/react-native, @json-render/react-email, @json-render/react-pdf, @json-render/image, @json-render/remotion, @json-render/codegen, @json-render/mcp, @json-render/redux, @json-render/zustand, @json-render/jotai, @json-render/xstate
+Skills: json-render ships AI agent skills that teach coding agents how to use each package. Install with "npx skills add vercel-labs/json-render --skill <name>". Available skills: core, react, react-pdf, react-email, react-native, shadcn, react-three-fiber, image, remotion, vue, svelte, solid, codegen, mcp, redux, zustand, jotai, xstate. See /docs/skills for details.
 
 You have access to the full json-render documentation via the bash and readFile tools. The docs are available as markdown files in the /workspace/docs/ directory.
 

+ 4 - 0
apps/web/lib/docs-navigation.ts

@@ -134,6 +134,10 @@ export const docsNavigation: NavSection[] = [
       { title: "@json-render/vue", href: "/docs/api/vue" },
       { title: "@json-render/svelte", href: "/docs/api/svelte" },
       { title: "@json-render/solid", href: "/docs/api/solid" },
+      {
+        title: "@json-render/react-three-fiber",
+        href: "/docs/api/react-three-fiber",
+      },
       { title: "@json-render/codegen", href: "/docs/api/codegen" },
       { title: "@json-render/mcp", href: "/docs/api/mcp" },
       { title: "@json-render/redux", href: "/docs/api/redux" },

+ 1 - 0
apps/web/lib/page-titles.ts

@@ -56,6 +56,7 @@ export const PAGE_TITLES: Record<string, string> = {
   "docs/api/redux": "@json-render/redux API",
   "docs/api/zustand": "@json-render/zustand API",
   "docs/api/jotai": "@json-render/jotai API",
+  "docs/api/react-three-fiber": "@json-render/react-three-fiber API",
   "docs/api/xstate": "@json-render/xstate API",
 };
 

+ 20 - 0
examples/react-three-fiber/app/layout.tsx

@@ -0,0 +1,20 @@
+import type { Metadata } from "next";
+
+export const metadata: Metadata = {
+  title: "json-render React Three Fiber Example",
+  description: "3D scenes from JSON specs with @json-render/react-three-fiber",
+};
+
+export default function RootLayout({
+  children,
+}: {
+  children: React.ReactNode;
+}) {
+  return (
+    <html lang="en">
+      <body style={{ margin: 0, fontFamily: "system-ui, sans-serif" }}>
+        {children}
+      </body>
+    </html>
+  );
+}

+ 238 - 0
examples/react-three-fiber/app/page.tsx

@@ -0,0 +1,238 @@
+"use client";
+
+import { useState } from "react";
+import { defineCatalog } from "@json-render/core";
+import type { ComputedFunction } from "@json-render/core";
+import { schema, defineRegistry } from "@json-render/react";
+import {
+  threeComponentDefinitions,
+  threeComponents,
+  ThreeCanvas,
+} from "@json-render/react-three-fiber";
+import { scenes } from "./scenes";
+
+const catalog = defineCatalog(schema, {
+  components: {
+    ...threeComponentDefinitions,
+  },
+  actions: {},
+});
+
+const { registry } = defineRegistry(catalog, {
+  components: {
+    ...threeComponents,
+  },
+});
+
+function num(v: unknown, fallback: number): number {
+  return typeof v === "number" ? v : fallback;
+}
+
+const computedFunctions: Record<string, ComputedFunction> = {
+  halfHeight: (a) => num(a.h, 1) / 2,
+
+  helixX: (a) => {
+    const angle =
+      (num(a.i, 0) / num(a.count, 40)) * num(a.turns, 3) * 2 * Math.PI +
+      num(a.strand, 0) * Math.PI;
+    return num(a.radius, 1.8) * Math.cos(angle);
+  },
+  helixY: (a) => num(a.i, 0) * num(a.spacing, 0.3) + num(a.offset, -6),
+  helixZ: (a) => {
+    const angle =
+      (num(a.i, 0) / num(a.count, 40)) * num(a.turns, 3) * 2 * Math.PI +
+      num(a.strand, 0) * Math.PI;
+    return num(a.radius, 1.8) * Math.sin(angle);
+  },
+  helixHue: (a) => {
+    const t = num(a.i, 0) / num(a.count, 40);
+    return `hsl(${Math.round(t * 270 + 180)}, 85%, 60%)`;
+  },
+
+  spiralX: (a) => {
+    const t = num(a.i, 0) / num(a.count, 60);
+    const r = 0.5 + t * num(a.maxRadius, 7);
+    const angle = t * num(a.turns, 4) * 2 * Math.PI;
+    return r * Math.cos(angle);
+  },
+  spiralZ: (a) => {
+    const t = num(a.i, 0) / num(a.count, 60);
+    const r = 0.5 + t * num(a.maxRadius, 7);
+    const angle = t * num(a.turns, 4) * 2 * Math.PI;
+    return r * Math.sin(angle);
+  },
+  spiralY: (a) => {
+    const i = num(a.i, 0);
+    return Math.sin(i * 1.7) * 0.4;
+  },
+  spiralScale: (a) => {
+    const t = num(a.i, 0) / num(a.count, 60);
+    return 0.08 + (1 - t) * 0.25;
+  },
+  spiralEmissive: (a) => {
+    const t = num(a.i, 0) / num(a.count, 60);
+    return Math.max(0, 1 - t * 1.5);
+  },
+
+  circleX: (a) => {
+    const angle = (num(a.i, 0) / num(a.count, 8)) * 2 * Math.PI;
+    return num(a.radius, 3) * Math.cos(angle);
+  },
+  circleZ: (a) => {
+    const angle = (num(a.i, 0) / num(a.count, 8)) * 2 * Math.PI;
+    return num(a.radius, 3) * Math.sin(angle);
+  },
+  circleY: (a) => {
+    const angle = (num(a.i, 0) / num(a.count, 8)) * 2 * Math.PI;
+    return Math.sin(angle * num(a.freq, 3)) * num(a.amp, 0.3);
+  },
+  circleAngle: (a) => {
+    return (num(a.i, 0) / num(a.count, 8)) * 2 * Math.PI;
+  },
+  circleHue: (a) => {
+    const t = num(a.i, 0) / num(a.count, 8);
+    return `hsl(${Math.round(t * 360)}, ${num(a.sat, 70)}%, ${num(a.lit, 80)}%)`;
+  },
+};
+
+function highlightJson(json: string): string {
+  return json.replace(
+    /("(?:\\.|[^"\\])*")\s*(:)|("(?:\\.|[^"\\])*")|([-+]?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)|(\btrue\b|\bfalse\b|\bnull\b)|([{}[\]:,])/g,
+    (_, key, colon, str, num, lit, punct) => {
+      if (key) return `<span style="color:#FF4D8D">${key}</span>${colon}`;
+      if (str) return `<span style="color:#00CA50">${str}</span>`;
+      if (num) return `<span style="color:#47A8FF">${num}</span>`;
+      if (lit) return `<span style="color:#47A8FF">${lit}</span>`;
+      if (punct) return `<span style="color:#666">${punct}</span>`;
+      return _;
+    },
+  );
+}
+
+const LIST_WIDTH = 220;
+const JSON_WIDTH = 380;
+const HEADER_HEIGHT = 40;
+
+const headerStyle: React.CSSProperties = {
+  height: HEADER_HEIGHT,
+  display: "flex",
+  alignItems: "center",
+  padding: "0 16px",
+  fontSize: 11,
+  fontWeight: 600,
+  color: "#555",
+  letterSpacing: "0.08em",
+  textTransform: "uppercase",
+  fontFamily: "ui-monospace, monospace",
+  borderBottom: "1px solid #1e1e1e",
+  flexShrink: 0,
+  whiteSpace: "nowrap",
+  overflow: "hidden",
+  textOverflow: "ellipsis",
+  boxSizing: "border-box",
+};
+
+export default function Page() {
+  const [selectedIndex, setSelectedIndex] = useState(0);
+  const selected = scenes[selectedIndex]!;
+
+  return (
+    <div style={{ height: "100vh", display: "flex", background: "#0a0a0a" }}>
+      <div
+        style={{
+          width: LIST_WIDTH,
+          flexShrink: 0,
+          display: "flex",
+          flexDirection: "column",
+          borderRight: "1px solid #1e1e1e",
+          background: "#0f0f0f",
+        }}
+      >
+        <div style={headerStyle}>Scenes</div>
+        <div style={{ flex: 1, overflowY: "auto", padding: "6px 0" }}>
+          {scenes.map((scene, i) => (
+            <button
+              key={scene.name}
+              onClick={() => setSelectedIndex(i)}
+              style={{
+                display: "block",
+                width: "100%",
+                padding: "8px 16px",
+                fontSize: 13,
+                border: "none",
+                textAlign: "left",
+                background:
+                  i === selectedIndex
+                    ? "rgba(255,255,255,0.08)"
+                    : "transparent",
+                color: i === selectedIndex ? "#fff" : "#888",
+                fontWeight: i === selectedIndex ? 500 : 400,
+                cursor: "pointer",
+                borderLeft:
+                  i === selectedIndex
+                    ? "2px solid #fff"
+                    : "2px solid transparent",
+                fontFamily: "inherit",
+              }}
+            >
+              {scene.name}
+            </button>
+          ))}
+        </div>
+      </div>
+
+      <div
+        style={{
+          flex: 1,
+          display: "flex",
+          flexDirection: "column",
+          minWidth: 0,
+        }}
+      >
+        <div style={headerStyle}>{selected.description}</div>
+        <div style={{ flex: 1, background: "#000", position: "relative" }}>
+          <ThreeCanvas
+            key={selectedIndex}
+            spec={selected.spec}
+            registry={registry}
+            functions={computedFunctions}
+            shadows
+            camera={{ position: [0, 0, 5], fov: 50 }}
+            style={{ width: "100%", height: "100%" }}
+          />
+        </div>
+      </div>
+
+      <div
+        style={{
+          width: JSON_WIDTH,
+          flexShrink: 0,
+          display: "flex",
+          flexDirection: "column",
+          borderLeft: "1px solid #1e1e1e",
+          background: "#0d0d0d",
+        }}
+      >
+        <div style={headerStyle}>Spec JSON</div>
+        <pre
+          style={{
+            flex: 1,
+            margin: 0,
+            padding: 14,
+            overflowY: "auto",
+            overflowX: "auto",
+            fontSize: 11,
+            lineHeight: 1.6,
+            fontFamily:
+              "ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace",
+            color: "#EDEDED",
+            tabSize: 2,
+          }}
+          dangerouslySetInnerHTML={{
+            __html: highlightJson(JSON.stringify(selected.spec, null, 2)),
+          }}
+        />
+      </div>
+    </div>
+  );
+}

+ 9 - 0
examples/react-three-fiber/app/scenes/_helpers.ts

@@ -0,0 +1,9 @@
+import type { Spec } from "@json-render/core";
+
+export type Scene = { name: string; description: string; spec: Spec };
+
+export const PI = Math.PI;
+
+export function range(n: number) {
+  return Array.from({ length: n }, (_, i) => ({ i }));
+}

+ 474 - 0
examples/react-three-fiber/app/scenes/clockwork-orrery.ts

@@ -0,0 +1,474 @@
+import type { Scene } from "./_helpers";
+
+export const clockworkOrrery: Scene = {
+  name: "Clockwork Orrery",
+  description:
+    "Interlocking brass/gold rings spinning at different tilts and speeds with orbiting metallic planets. Like the inside of a celestial clockwork mechanism.",
+  spec: {
+    root: "scene",
+    elements: {
+      scene: {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "cam",
+          "env",
+          "ambient",
+          "key",
+          "warm",
+          "accent",
+          "center",
+          "ring-spin-0",
+          "ring-spin-1",
+          "ring-spin-2",
+          "ring-spin-3",
+          "planet-orbit-0",
+          "planet-orbit-1",
+          "planet-orbit-2",
+          "sparkles",
+          "post",
+          "controls",
+        ],
+      },
+      cam: {
+        type: "PerspectiveCamera",
+        props: {
+          position: [0, 3, 8],
+          fov: 50,
+          makeDefault: true,
+        },
+        children: [],
+      },
+      env: {
+        type: "Environment",
+        props: {
+          preset: "dawn",
+          background: true,
+          blur: 1,
+          intensity: 0.5,
+        },
+        children: [],
+      },
+      ambient: {
+        type: "AmbientLight",
+        props: { color: "#332200", intensity: 0.4 },
+        children: [],
+      },
+      key: {
+        type: "DirectionalLight",
+        props: {
+          position: [5, 10, 5],
+          rotation: null,
+          scale: null,
+          color: "#ffddaa",
+          intensity: 2,
+          castShadow: true,
+        },
+        children: [],
+      },
+      warm: {
+        type: "PointLight",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          color: "#ffaa44",
+          intensity: 30,
+          distance: 15,
+          decay: 2,
+          castShadow: false,
+        },
+        children: [],
+      },
+      accent: {
+        type: "PointLight",
+        props: {
+          position: [-4, 3, -4],
+          rotation: null,
+          scale: null,
+          color: "#ffcc88",
+          intensity: 15,
+          distance: 15,
+          decay: 2,
+          castShadow: false,
+        },
+        children: [],
+      },
+      center: {
+        type: "Float",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 0.5,
+          rotationIntensity: 0.1,
+          floatIntensity: 0.2,
+          enabled: true,
+        },
+        children: ["center-sphere"],
+      },
+      "center-sphere": {
+        type: "GlassSphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          radius: 0.5,
+          widthSegments: 32,
+          heightSegments: 16,
+          color: "#ffddaa",
+          transmission: 1,
+          thickness: 0.8,
+          roughness: 0,
+          chromaticAberration: 0.1,
+          ior: 1.8,
+          distortion: 0.05,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 10,
+          resolution: 256,
+        },
+        children: [],
+      },
+      "ring-spin-0": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: [0.3, 0, 0.8],
+          scale: null,
+          speed: 1.2,
+          axis: "y",
+        },
+        children: ["ring-torus-0"],
+      },
+      "ring-torus-0": {
+        type: "Torus",
+        props: {
+          position: null,
+          rotation: [1.5708, 0, 0],
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          material: {
+            color: "#ffcc44",
+            metalness: 0.95,
+            roughness: 0.1,
+            emissive: "#cc9900",
+            emissiveIntensity: 0.4,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 2,
+          tube: 0.04,
+          radialSegments: null,
+          tubularSegments: 128,
+        },
+        children: [],
+      },
+      "ring-spin-1": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: [0.9, 0, -0.2],
+          scale: null,
+          speed: -0.7,
+          axis: "y",
+        },
+        children: ["ring-torus-1"],
+      },
+      "ring-torus-1": {
+        type: "Torus",
+        props: {
+          position: null,
+          rotation: [1.5708, 0, 0],
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          material: {
+            color: "#cc8833",
+            metalness: 0.95,
+            roughness: 0.1,
+            emissive: "#aa6622",
+            emissiveIntensity: 0.3,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 3.2,
+          tube: 0.035,
+          radialSegments: null,
+          tubularSegments: 128,
+        },
+        children: [],
+      },
+      "ring-spin-2": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: [-0.4, 0, 0.5],
+          scale: null,
+          speed: 0.4,
+          axis: "y",
+        },
+        children: ["ring-torus-2"],
+      },
+      "ring-torus-2": {
+        type: "Torus",
+        props: {
+          position: null,
+          rotation: [1.5708, 0, 0],
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          material: {
+            color: "#bb9944",
+            metalness: 0.95,
+            roughness: 0.1,
+            emissive: "#886622",
+            emissiveIntensity: 0.25,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 4.5,
+          tube: 0.03,
+          radialSegments: null,
+          tubularSegments: 128,
+        },
+        children: [],
+      },
+      "ring-spin-3": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: [0.1, 0, -0.1],
+          scale: null,
+          speed: -0.2,
+          axis: "y",
+        },
+        children: ["ring-torus-3"],
+      },
+      "ring-torus-3": {
+        type: "Torus",
+        props: {
+          position: null,
+          rotation: [1.5708, 0, 0],
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          material: {
+            color: "#997744",
+            metalness: 0.95,
+            roughness: 0.1,
+            emissive: "#664422",
+            emissiveIntensity: 0.15,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 6,
+          tube: 0.025,
+          radialSegments: null,
+          tubularSegments: 128,
+        },
+        children: [],
+      },
+      "planet-orbit-0": {
+        type: "Orbit",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          speed: 0.6,
+          radius: 2.5,
+          tilt: 0.3,
+        },
+        children: ["planet-spin-0"],
+      },
+      "planet-spin-0": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 3,
+          axis: "y",
+        },
+        children: ["planet-sphere-0"],
+      },
+      "planet-sphere-0": {
+        type: "Sphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          material: {
+            color: "#ffffff",
+            metalness: 1,
+            roughness: 0.02,
+            emissive: "#ffffff",
+            emissiveIntensity: 0.2,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 0.2,
+          widthSegments: 32,
+          heightSegments: 16,
+        },
+        children: [],
+      },
+      "planet-orbit-1": {
+        type: "Orbit",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          speed: -0.9,
+          radius: 3.8,
+          tilt: 0.8,
+        },
+        children: ["planet-spin-1"],
+      },
+      "planet-spin-1": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: -2,
+          axis: "x",
+        },
+        children: ["planet-sphere-1"],
+      },
+      "planet-sphere-1": {
+        type: "Sphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          material: {
+            color: "#ffcc88",
+            metalness: 0.8,
+            roughness: 0.15,
+            emissive: "#ffcc88",
+            emissiveIntensity: 0.2,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 0.15,
+          widthSegments: 32,
+          heightSegments: 16,
+        },
+        children: [],
+      },
+      "planet-orbit-2": {
+        type: "Orbit",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          speed: 0.35,
+          radius: 5.2,
+          tilt: -0.5,
+        },
+        children: ["planet-spin-2"],
+      },
+      "planet-spin-2": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 1.5,
+          axis: "z",
+        },
+        children: ["planet-sphere-2"],
+      },
+      "planet-sphere-2": {
+        type: "Sphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          material: {
+            color: "#ccaa66",
+            metalness: 0.9,
+            roughness: 0.05,
+            emissive: "#ccaa66",
+            emissiveIntensity: 0.2,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 0.18,
+          widthSegments: 32,
+          heightSegments: 16,
+        },
+        children: [],
+      },
+      sparkles: {
+        type: "Sparkles",
+        props: {
+          position: null,
+          rotation: null,
+          scale: [14, 10, 14],
+          count: 150,
+          speed: 0.15,
+          opacity: 0.3,
+          color: "#ffcc66",
+          size: 1,
+          noise: 2,
+        },
+        children: [],
+      },
+      post: {
+        type: "EffectComposer",
+        props: { enabled: true, multisampling: 8 },
+        children: ["bloom", "vignette"],
+      },
+      bloom: {
+        type: "Bloom",
+        props: {
+          intensity: 1.2,
+          luminanceThreshold: 0.2,
+          luminanceSmoothing: null,
+          mipmapBlur: true,
+        },
+        children: [],
+      },
+      vignette: {
+        type: "Vignette",
+        props: { offset: 0.5, darkness: 0.5 },
+        children: [],
+      },
+      controls: {
+        type: "OrbitControls",
+        props: {
+          enableDamping: true,
+          dampingFactor: null,
+          enableZoom: true,
+          enablePan: null,
+          enableRotate: true,
+          minDistance: 4,
+          maxDistance: 18,
+          minPolarAngle: 0.3,
+          maxPolarAngle: 1.5,
+          autoRotate: true,
+          autoRotateSpeed: 0.3,
+        },
+        children: [],
+      },
+    },
+  },
+};

+ 692 - 0
examples/react-three-fiber/app/scenes/deep-sea-abyss.ts

@@ -0,0 +1,692 @@
+import type { Scene } from "./_helpers";
+
+export const deepSeaAbyss: Scene = {
+  name: "Deep Sea Abyss",
+  description:
+    "Bioluminescent deep ocean. Pitch black with glowing jellyfish-like creatures.",
+  spec: {
+    root: "scene",
+    elements: {
+      scene: {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "cam",
+          "env",
+          "fog",
+          "ambient",
+          "jelly-1-pulse",
+          "jelly-2-pulse",
+          "jelly-3-pulse",
+          "light-1",
+          "light-2",
+          "light-3",
+          "tentacles-1",
+          "tentacles-2",
+          "tentacles-3",
+          "sparkles-1",
+          "sparkles-2",
+          "controls",
+        ],
+      },
+      cam: {
+        type: "PerspectiveCamera",
+        props: {
+          position: [0, 1, 6],
+          fov: 55,
+          makeDefault: true,
+        },
+        children: [],
+      },
+      env: {
+        type: "Environment",
+        props: {
+          preset: "night",
+          background: false,
+          blur: 1,
+          intensity: 0.05,
+        },
+        children: [],
+      },
+      fog: {
+        type: "Fog",
+        props: { color: "#000510", near: 2, far: 18 },
+        children: [],
+      },
+      ambient: {
+        type: "AmbientLight",
+        props: { color: "#001133", intensity: 0.15 },
+        children: [],
+      },
+      "jelly-1-pulse": {
+        type: "Pulse",
+        props: {
+          position: [0, 1, 0],
+          rotation: null,
+          scale: null,
+          speed: 0.6,
+          min: 0.85,
+          max: 1.15,
+        },
+        children: ["jelly-1-spin"],
+      },
+      "jelly-1-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 0.2,
+          axis: "y",
+        },
+        children: ["jelly-1-body"],
+      },
+      "jelly-1-body": {
+        type: "DistortSphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 1,
+          widthSegments: 64,
+          heightSegments: 32,
+          color: "#00ccff",
+          speed: 2,
+          distort: 0.5,
+          metalness: 0,
+          roughness: 0.2,
+        },
+        children: [],
+      },
+      "jelly-2-pulse": {
+        type: "Pulse",
+        props: {
+          position: [-4, -1, -3],
+          rotation: null,
+          scale: null,
+          speed: 0.8,
+          min: 0.8,
+          max: 1.2,
+        },
+        children: ["jelly-2-spin"],
+      },
+      "jelly-2-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: -0.3,
+          axis: "y",
+        },
+        children: ["jelly-2-body"],
+      },
+      "jelly-2-body": {
+        type: "DistortSphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.7,
+          widthSegments: 64,
+          heightSegments: 32,
+          color: "#cc44ff",
+          speed: 2.5,
+          distort: 0.6,
+          metalness: 0,
+          roughness: 0.2,
+        },
+        children: [],
+      },
+      "jelly-3-pulse": {
+        type: "Pulse",
+        props: {
+          position: [3, 0.5, -5],
+          rotation: null,
+          scale: null,
+          speed: 0.5,
+          min: 0.9,
+          max: 1.1,
+        },
+        children: ["jelly-3-spin"],
+      },
+      "jelly-3-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 0.15,
+          axis: "y",
+        },
+        children: ["jelly-3-body"],
+      },
+      "jelly-3-body": {
+        type: "DistortSphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.5,
+          widthSegments: 64,
+          heightSegments: 32,
+          color: "#ff44aa",
+          speed: 1.5,
+          distort: 0.4,
+          metalness: 0,
+          roughness: 0.3,
+        },
+        children: [],
+      },
+      "light-1": {
+        type: "PointLight",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          color: "#00ccff",
+          intensity: 20,
+          distance: 12,
+          decay: 2,
+          castShadow: null,
+        },
+        children: [],
+      },
+      "light-2": {
+        type: "PointLight",
+        props: {
+          position: [-4, -1, -3],
+          rotation: null,
+          scale: null,
+          color: "#cc44ff",
+          intensity: 15,
+          distance: 10,
+          decay: 2,
+          castShadow: null,
+        },
+        children: [],
+      },
+      "light-3": {
+        type: "PointLight",
+        props: {
+          position: [3, 0.5, -5],
+          rotation: null,
+          scale: null,
+          color: "#ff44aa",
+          intensity: 10,
+          distance: 8,
+          decay: 2,
+          castShadow: null,
+        },
+        children: [],
+      },
+      "tentacles-1": {
+        type: "Float",
+        props: {
+          position: [0, -1, 0],
+          rotation: null,
+          scale: null,
+          speed: 0.3,
+          rotationIntensity: 0.5,
+          floatIntensity: 0.8,
+          enabled: true,
+        },
+        children: ["t1-s1", "t1-s2", "t1-s3", "t1-s4", "t1-s5", "t1-s6"],
+      },
+      "t1-s1": {
+        type: "GlassSphere",
+        props: {
+          position: [0.3, -0.5, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.12,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#00ccff",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "t1-s2": {
+        type: "GlassSphere",
+        props: {
+          position: [-0.2, -1.2, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.1,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#00ccff",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "t1-s3": {
+        type: "GlassSphere",
+        props: {
+          position: [0.4, -1.9, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.08,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#00ccff",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "t1-s4": {
+        type: "GlassSphere",
+        props: {
+          position: [-0.3, -2.5, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.07,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#00ccff",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "t1-s5": {
+        type: "GlassSphere",
+        props: {
+          position: [0.1, -3.0, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.06,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#00ccff",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "t1-s6": {
+        type: "GlassSphere",
+        props: {
+          position: [-0.1, -3.4, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.05,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#00ccff",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "tentacles-2": {
+        type: "Float",
+        props: {
+          position: [-4, -2.5, -3],
+          rotation: null,
+          scale: null,
+          speed: 0.4,
+          rotationIntensity: 0.5,
+          floatIntensity: 0.8,
+          enabled: true,
+        },
+        children: ["t2-s1", "t2-s2", "t2-s3", "t2-s4", "t2-s5"],
+      },
+      "t2-s1": {
+        type: "GlassSphere",
+        props: {
+          position: [0.3, -0.5, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.1,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#cc44ff",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "t2-s2": {
+        type: "GlassSphere",
+        props: {
+          position: [-0.2, -1.1, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.08,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#cc44ff",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "t2-s3": {
+        type: "GlassSphere",
+        props: {
+          position: [0.4, -1.7, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.06,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#cc44ff",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "t2-s4": {
+        type: "GlassSphere",
+        props: {
+          position: [-0.3, -2.2, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.05,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#cc44ff",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "t2-s5": {
+        type: "GlassSphere",
+        props: {
+          position: [0.1, -2.6, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.05,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#cc44ff",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "tentacles-3": {
+        type: "Float",
+        props: {
+          position: [3, -1, -5],
+          rotation: null,
+          scale: null,
+          speed: 0.35,
+          rotationIntensity: 0.5,
+          floatIntensity: 0.8,
+          enabled: true,
+        },
+        children: ["t3-s1", "t3-s2", "t3-s3", "t3-s4"],
+      },
+      "t3-s1": {
+        type: "GlassSphere",
+        props: {
+          position: [0.3, -0.5, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.09,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#ff44aa",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "t3-s2": {
+        type: "GlassSphere",
+        props: {
+          position: [-0.2, -1.0, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.07,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#ff44aa",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "t3-s3": {
+        type: "GlassSphere",
+        props: {
+          position: [0.4, -1.5, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.06,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#ff44aa",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "t3-s4": {
+        type: "GlassSphere",
+        props: {
+          position: [-0.1, -1.9, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.05,
+          widthSegments: 16,
+          heightSegments: 8,
+          color: "#ff44aa",
+          transmission: 0.9,
+          thickness: 0.3,
+          roughness: 0.1,
+          chromaticAberration: 0.1,
+          ior: 1.5,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      "sparkles-1": {
+        type: "Sparkles",
+        props: {
+          position: [0, 0, -3],
+          rotation: null,
+          scale: [15, 10, 15],
+          count: 300,
+          speed: 0.1,
+          opacity: 0.4,
+          color: "#00aaff",
+          size: 1,
+          noise: 3,
+        },
+        children: [],
+      },
+      "sparkles-2": {
+        type: "Sparkles",
+        props: {
+          position: [0, -2, 0],
+          rotation: null,
+          scale: [12, 6, 12],
+          count: 150,
+          speed: 0.05,
+          opacity: 0.2,
+          color: "#8844ff",
+          size: 0.8,
+          noise: 2,
+        },
+        children: [],
+      },
+      controls: {
+        type: "OrbitControls",
+        props: {
+          enableDamping: true,
+          dampingFactor: null,
+          enableZoom: true,
+          enablePan: null,
+          enableRotate: true,
+          minDistance: 3,
+          maxDistance: 18,
+          minPolarAngle: null,
+          maxPolarAngle: null,
+          autoRotate: true,
+          autoRotateSpeed: 0.15,
+        },
+        children: [],
+      },
+    },
+  },
+};

+ 940 - 0
examples/react-three-fiber/app/scenes/floating-islands.ts

@@ -0,0 +1,940 @@
+import { PI } from "./_helpers";
+import type { Scene } from "./_helpers";
+
+export const floatingIslands: Scene = {
+  name: "Floating Islands",
+  description:
+    "Sky, clouds, islands with trees and bridges floating in sunset light",
+  spec: {
+    root: "scene",
+    elements: {
+      scene: {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "cam",
+          "sky",
+          "ambient",
+          "sun",
+          "clouds",
+          "island-main-float",
+          "island-left-float",
+          "island-right-float",
+          "island-far-float",
+          "island-tiny1-float",
+          "island-tiny2-float",
+          "bridge-1",
+          "bridge-2",
+          "sparkles",
+          "controls",
+        ],
+      },
+      cam: {
+        type: "PerspectiveCamera",
+        props: {
+          position: [8, 6, 14],
+          rotation: null,
+          scale: null,
+          fov: 50,
+          near: 0.1,
+          far: 500,
+          makeDefault: true,
+        },
+        children: [],
+      },
+      sky: {
+        type: "Environment",
+        props: {
+          preset: "dawn",
+          background: true,
+          blur: 0.8,
+          intensity: 0.5,
+        },
+        children: [],
+      },
+      ambient: {
+        type: "AmbientLight",
+        props: { color: "#ffd4a0", intensity: 0.5 },
+        children: [],
+      },
+      sun: {
+        type: "DirectionalLight",
+        props: {
+          position: [8, 12, 6],
+          rotation: null,
+          scale: null,
+          color: "#ffcc88",
+          intensity: 2.5,
+          castShadow: true,
+        },
+        children: [],
+      },
+      clouds: {
+        type: "Cloud",
+        props: {
+          position: [0, 4, -8],
+          rotation: null,
+          scale: null,
+          seed: 7,
+          segments: 40,
+          bounds: [25, 3, 20],
+          volume: 10,
+          speed: 0.1,
+          fade: 25,
+          opacity: 0.6,
+          color: "#ffe8d0",
+          growth: 8,
+        },
+        children: [],
+      },
+
+      "island-main-float": {
+        type: "Float",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          speed: 0.4,
+          rotationIntensity: 0.02,
+          floatIntensity: 0.3,
+          enabled: true,
+        },
+        children: ["island-main"],
+      },
+      "island-main": {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "main-base",
+          "main-top",
+          "main-underhang",
+          "tree-1-trunk",
+          "tree-1-canopy",
+          "tree-2-trunk",
+          "tree-2-canopy",
+          "tree-3-trunk",
+          "tree-3-canopy",
+          "house-base",
+          "house-roof",
+        ],
+      },
+      "main-base": {
+        type: "Cylinder",
+        props: {
+          position: [0, -0.5, 0],
+          rotation: null,
+          scale: null,
+          castShadow: true,
+          receiveShadow: true,
+          material: {
+            color: "#8B6914",
+            metalness: 0.1,
+            roughness: 0.9,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radiusTop: 3,
+          radiusBottom: 1.5,
+          height: 2,
+          radialSegments: 16,
+        },
+        children: [],
+      },
+      "main-top": {
+        type: "Cylinder",
+        props: {
+          position: [0, 0.55, 0],
+          rotation: null,
+          scale: null,
+          castShadow: true,
+          receiveShadow: true,
+          material: {
+            color: "#4a8c3f",
+            metalness: 0,
+            roughness: 0.95,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radiusTop: 3.1,
+          radiusBottom: 3,
+          height: 0.3,
+          radialSegments: 16,
+        },
+        children: [],
+      },
+      "main-underhang": {
+        type: "Cone",
+        props: {
+          position: [0, -2.5, 0],
+          rotation: [PI, 0, 0],
+          scale: null,
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#6B4513",
+            metalness: 0.1,
+            roughness: 0.95,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 1.5,
+          height: 2.5,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "tree-1-trunk": {
+        type: "Cylinder",
+        props: {
+          position: [1, 1, 0.5],
+          rotation: null,
+          scale: null,
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#8B5A2B",
+            metalness: 0,
+            roughness: 1,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radiusTop: 0.06,
+          radiusBottom: 0.1,
+          height: 1.5,
+          radialSegments: 8,
+        },
+        children: [],
+      },
+      "tree-1-canopy": {
+        type: "Sphere",
+        props: {
+          position: [1, 2.2, 0.5],
+          rotation: null,
+          scale: [1, 1.2, 1],
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#2d7a2d",
+            metalness: 0,
+            roughness: 0.95,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 0.6,
+          widthSegments: 16,
+          heightSegments: 12,
+        },
+        children: [],
+      },
+      "tree-2-trunk": {
+        type: "Cylinder",
+        props: {
+          position: [-1.2, 0.8, -0.8],
+          rotation: null,
+          scale: null,
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#8B5A2B",
+            metalness: 0,
+            roughness: 1,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radiusTop: 0.05,
+          radiusBottom: 0.08,
+          height: 1.1,
+          radialSegments: 8,
+        },
+        children: [],
+      },
+      "tree-2-canopy": {
+        type: "Sphere",
+        props: {
+          position: [-1.2, 1.7, -0.8],
+          rotation: null,
+          scale: [1, 1.3, 1],
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#3a8c3a",
+            metalness: 0,
+            roughness: 0.95,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 0.45,
+          widthSegments: 16,
+          heightSegments: 12,
+        },
+        children: [],
+      },
+      "tree-3-trunk": {
+        type: "Cylinder",
+        props: {
+          position: [-0.3, 1.2, 1.5],
+          rotation: null,
+          scale: null,
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#8B5A2B",
+            metalness: 0,
+            roughness: 1,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radiusTop: 0.07,
+          radiusBottom: 0.12,
+          height: 2,
+          radialSegments: 8,
+        },
+        children: [],
+      },
+      "tree-3-canopy": {
+        type: "Sphere",
+        props: {
+          position: [-0.3, 2.7, 1.5],
+          rotation: null,
+          scale: [1.2, 1.4, 1.2],
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#1a6b1a",
+            metalness: 0,
+            roughness: 0.95,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 0.7,
+          widthSegments: 16,
+          heightSegments: 12,
+        },
+        children: [],
+      },
+      "house-base": {
+        type: "RoundedBox",
+        props: {
+          position: [0, 0.9, -0.5],
+          rotation: [0, 0.3, 0],
+          scale: null,
+          castShadow: true,
+          receiveShadow: true,
+          material: {
+            color: "#e8d8c0",
+            metalness: 0,
+            roughness: 0.8,
+            emissive: "#ffddaa",
+            emissiveIntensity: 0.05,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          width: 0.8,
+          height: 0.7,
+          depth: 0.7,
+          radius: 0.05,
+          smoothness: 4,
+        },
+        children: [],
+      },
+      "house-roof": {
+        type: "Cone",
+        props: {
+          position: [0, 1.55, -0.5],
+          rotation: [0, 1.085, 0],
+          scale: null,
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#a04030",
+            metalness: 0.1,
+            roughness: 0.8,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 0.6,
+          height: 0.5,
+          radialSegments: 4,
+        },
+        children: [],
+      },
+
+      "island-left-float": {
+        type: "Float",
+        props: {
+          position: [-6, 0.1, 2],
+          rotation: null,
+          scale: null,
+          speed: 0.6,
+          rotationIntensity: 0.03,
+          floatIntensity: 0.5,
+          enabled: true,
+        },
+        children: ["island-left"],
+      },
+      "island-left": {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "left-base",
+          "left-top",
+          "left-under",
+          "left-tree-t",
+          "left-tree-c",
+        ],
+      },
+      "left-base": {
+        type: "Cylinder",
+        props: {
+          position: [0, -0.3, 0],
+          rotation: null,
+          scale: null,
+          castShadow: true,
+          receiveShadow: true,
+          material: {
+            color: "#8B6914",
+            metalness: 0.1,
+            roughness: 0.9,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radiusTop: 1.8,
+          radiusBottom: 0.8,
+          height: 1.2,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "left-top": {
+        type: "Cylinder",
+        props: {
+          position: [0, 0.25, 0],
+          rotation: null,
+          scale: null,
+          castShadow: true,
+          receiveShadow: true,
+          material: {
+            color: "#4a8c3f",
+            metalness: 0,
+            roughness: 0.95,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radiusTop: 1.85,
+          radiusBottom: 1.8,
+          height: 0.2,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "left-under": {
+        type: "Cone",
+        props: {
+          position: [0, -1.5, 0],
+          rotation: [PI, 0, 0],
+          scale: null,
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#6B4513",
+            metalness: 0.1,
+            roughness: 0.95,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 0.8,
+          height: 1.5,
+          radialSegments: 10,
+        },
+        children: [],
+      },
+      "left-tree-t": {
+        type: "Cylinder",
+        props: {
+          position: [0.3, 0.8, 0],
+          rotation: null,
+          scale: null,
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#8B5A2B",
+            metalness: 0,
+            roughness: 1,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radiusTop: 0.04,
+          radiusBottom: 0.07,
+          height: 1,
+          radialSegments: 8,
+        },
+        children: [],
+      },
+      "left-tree-c": {
+        type: "Sphere",
+        props: {
+          position: [0.3, 1.6, 0],
+          rotation: null,
+          scale: [1, 1.2, 1],
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#2d7a2d",
+            metalness: 0,
+            roughness: 0.95,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 0.4,
+          widthSegments: 12,
+          heightSegments: 8,
+        },
+        children: [],
+      },
+
+      "island-right-float": {
+        type: "Float",
+        props: {
+          position: [5.5, 0, -3],
+          rotation: null,
+          scale: null,
+          speed: 0.5,
+          rotationIntensity: 0.02,
+          floatIntensity: 0.4,
+          enabled: true,
+        },
+        children: ["island-right"],
+      },
+      "island-right": {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "right-base",
+          "right-top",
+          "right-under",
+          "right-tree-t",
+          "right-tree-c",
+        ],
+      },
+      "right-base": {
+        type: "Cylinder",
+        props: {
+          position: [0, -0.3, 0],
+          rotation: null,
+          scale: null,
+          castShadow: true,
+          receiveShadow: true,
+          material: {
+            color: "#8B6914",
+            metalness: 0.1,
+            roughness: 0.9,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radiusTop: 2,
+          radiusBottom: 1,
+          height: 1.5,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "right-top": {
+        type: "Cylinder",
+        props: {
+          position: [0, 0.4, 0],
+          rotation: null,
+          scale: null,
+          castShadow: true,
+          receiveShadow: true,
+          material: {
+            color: "#4a8c3f",
+            metalness: 0,
+            roughness: 0.95,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radiusTop: 2.05,
+          radiusBottom: 2,
+          height: 0.2,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "right-under": {
+        type: "Cone",
+        props: {
+          position: [0, -2, 0],
+          rotation: [PI, 0, 0],
+          scale: null,
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#6B4513",
+            metalness: 0.1,
+            roughness: 0.95,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 1,
+          height: 2,
+          radialSegments: 10,
+        },
+        children: [],
+      },
+      "right-tree-t": {
+        type: "Cylinder",
+        props: {
+          position: [-0.5, 0.9, 0.3],
+          rotation: null,
+          scale: null,
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#8B5A2B",
+            metalness: 0,
+            roughness: 1,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radiusTop: 0.05,
+          radiusBottom: 0.09,
+          height: 1.3,
+          radialSegments: 8,
+        },
+        children: [],
+      },
+      "right-tree-c": {
+        type: "Sphere",
+        props: {
+          position: [-0.5, 2, 0.3],
+          rotation: null,
+          scale: [1, 1.3, 1],
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#3a8c3a",
+            metalness: 0,
+            roughness: 0.95,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 0.5,
+          widthSegments: 12,
+          heightSegments: 8,
+        },
+        children: [],
+      },
+
+      "island-far-float": {
+        type: "Float",
+        props: {
+          position: [2, 3, -10],
+          rotation: null,
+          scale: [0.7, 0.7, 0.7],
+          speed: 0.3,
+          rotationIntensity: 0.01,
+          floatIntensity: 0.3,
+          enabled: true,
+        },
+        children: ["far-base", "far-top", "far-under"],
+      },
+      "far-base": {
+        type: "Cylinder",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          castShadow: true,
+          receiveShadow: true,
+          material: {
+            color: "#8B6914",
+            metalness: 0.1,
+            roughness: 0.9,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radiusTop: 2.5,
+          radiusBottom: 1.2,
+          height: 1.5,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "far-top": {
+        type: "Cylinder",
+        props: {
+          position: [0, 0.65, 0],
+          rotation: null,
+          scale: null,
+          castShadow: true,
+          receiveShadow: true,
+          material: {
+            color: "#3a7a3a",
+            metalness: 0,
+            roughness: 0.95,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radiusTop: 2.55,
+          radiusBottom: 2.5,
+          height: 0.2,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "far-under": {
+        type: "Cone",
+        props: {
+          position: [0, -1.8, 0],
+          rotation: [PI, 0, 0],
+          scale: null,
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#6B4513",
+            metalness: 0.1,
+            roughness: 0.95,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 1.2,
+          height: 2,
+          radialSegments: 10,
+        },
+        children: [],
+      },
+
+      "island-tiny1-float": {
+        type: "Float",
+        props: {
+          position: [-3, 2.5, -5],
+          rotation: null,
+          scale: [0.4, 0.4, 0.4],
+          speed: 0.8,
+          rotationIntensity: 0.05,
+          floatIntensity: 0.8,
+          enabled: true,
+        },
+        children: ["tiny1-rock"],
+      },
+      "tiny1-rock": {
+        type: "Sphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: [1, 0.6, 1],
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#8B6914",
+            metalness: 0.1,
+            roughness: 0.9,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 1,
+          widthSegments: 12,
+          heightSegments: 8,
+        },
+        children: [],
+      },
+      "island-tiny2-float": {
+        type: "Float",
+        props: {
+          position: [8, -0.5, 1],
+          rotation: null,
+          scale: [0.3, 0.3, 0.3],
+          speed: 1,
+          rotationIntensity: 0.08,
+          floatIntensity: 1,
+          enabled: true,
+        },
+        children: ["tiny2-rock"],
+      },
+      "tiny2-rock": {
+        type: "Sphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: [1.2, 0.5, 0.8],
+          castShadow: true,
+          receiveShadow: false,
+          material: {
+            color: "#7a5a10",
+            metalness: 0.1,
+            roughness: 0.9,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 1,
+          widthSegments: 12,
+          heightSegments: 8,
+        },
+        children: [],
+      },
+
+      "bridge-1": {
+        type: "RoundedBox",
+        props: {
+          position: [-3.57, -0.1, 1.19],
+          rotation: [0, 0.32, 0],
+          scale: null,
+          castShadow: true,
+          receiveShadow: true,
+          material: {
+            color: "#8B6914",
+            metalness: 0.1,
+            roughness: 0.9,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          width: 3.5,
+          height: 0.5,
+          depth: 0.6,
+          radius: 0.05,
+          smoothness: 2,
+        },
+        children: [],
+      },
+      "bridge-2": {
+        type: "RoundedBox",
+        props: {
+          position: [3.19, -0.1, -1.74],
+          rotation: [0, 0.5, 0],
+          scale: null,
+          castShadow: true,
+          receiveShadow: true,
+          material: {
+            color: "#8B6914",
+            metalness: 0.1,
+            roughness: 0.9,
+            emissive: null,
+            emissiveIntensity: null,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          width: 3.0,
+          height: 0.5,
+          depth: 0.6,
+          radius: 0.05,
+          smoothness: 2,
+        },
+        children: [],
+      },
+
+      sparkles: {
+        type: "Sparkles",
+        props: {
+          position: [0, 2, 0],
+          rotation: null,
+          scale: [20, 8, 20],
+          count: 120,
+          speed: 0.1,
+          opacity: 0.5,
+          color: "#ffeeaa",
+          size: 1.5,
+          noise: 1,
+        },
+        children: [],
+      },
+      controls: {
+        type: "OrbitControls",
+        props: {
+          enableDamping: true,
+          dampingFactor: null,
+          enableZoom: true,
+          enablePan: null,
+          enableRotate: true,
+          minDistance: 8,
+          maxDistance: 30,
+          minPolarAngle: 0.2,
+          maxPolarAngle: 1.5,
+          autoRotate: true,
+          autoRotateSpeed: 0.3,
+          target: [0, 1, 0],
+        },
+        children: [],
+      },
+    },
+  },
+};

+ 164 - 0
examples/react-three-fiber/app/scenes/hyperspace-tunnel.ts

@@ -0,0 +1,164 @@
+import type { Scene } from "./_helpers";
+
+export const hyperspaceTunnel: Scene = {
+  name: "Hyperspace Tunnel",
+  description:
+    "Neon warp tunnel at breakneck speed -- bloom, camera shake, particles streaming past",
+  spec: {
+    root: "scene",
+    elements: {
+      scene: {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "cam",
+          "controls",
+          "tunnel",
+          "stars",
+          "sparkles-core",
+          "sparkles-wide",
+          "ambient",
+          "center-light",
+          "shake",
+          "post",
+          "fog",
+        ],
+      },
+      cam: {
+        type: "PerspectiveCamera",
+        props: {
+          position: [0, 0, 2],
+          rotation: null,
+          scale: null,
+          fov: 80,
+          near: 0.1,
+          far: 200,
+          makeDefault: true,
+        },
+        children: [],
+      },
+      controls: {
+        type: "OrbitControls",
+        props: {
+          enableRotate: false,
+          enableZoom: false,
+          enablePan: false,
+        },
+        children: [],
+      },
+      tunnel: {
+        type: "WarpTunnel",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          ringCount: 100,
+          radius: 3,
+          length: 50,
+          speed: 12,
+          tubeRadius: 0.025,
+          color1: "#00ffff",
+          color2: "#ff00ff",
+        },
+        children: [],
+      },
+      stars: {
+        type: "Stars",
+        props: {
+          radius: 80,
+          depth: 60,
+          count: 6000,
+          factor: 5,
+          saturation: 0.4,
+          fade: true,
+          speed: 3,
+        },
+        children: [],
+      },
+      "sparkles-core": {
+        type: "Sparkles",
+        props: {
+          position: [0, 0, -10],
+          rotation: null,
+          scale: [4, 4, 30],
+          count: 300,
+          speed: 3,
+          opacity: 1,
+          color: "#00ffff",
+          size: 2,
+          noise: 0.5,
+        },
+        children: [],
+      },
+      "sparkles-wide": {
+        type: "Sparkles",
+        props: {
+          position: [0, 0, -15],
+          rotation: null,
+          scale: [6, 6, 40],
+          count: 200,
+          speed: 4,
+          opacity: 0.7,
+          color: "#ff44ff",
+          size: 1.5,
+          noise: 1,
+        },
+        children: [],
+      },
+      ambient: {
+        type: "AmbientLight",
+        props: { color: "#110033", intensity: 0.2 },
+        children: [],
+      },
+      "center-light": {
+        type: "PointLight",
+        props: {
+          position: [0, 0, -5],
+          rotation: null,
+          scale: null,
+          color: "#00ccff",
+          intensity: 30,
+          distance: 30,
+          decay: 2,
+          castShadow: false,
+        },
+        children: [],
+      },
+      shake: {
+        type: "CameraShake",
+        props: {
+          intensity: 0.3,
+          maxYaw: 0.03,
+          maxPitch: 0.03,
+          maxRoll: 0.02,
+        },
+        children: [],
+      },
+      post: {
+        type: "EffectComposer",
+        props: { enabled: true, multisampling: 8 },
+        children: ["bloom", "vignette"],
+      },
+      bloom: {
+        type: "Bloom",
+        props: {
+          intensity: 2.5,
+          luminanceThreshold: 0.1,
+          luminanceSmoothing: null,
+          mipmapBlur: true,
+        },
+        children: [],
+      },
+      vignette: {
+        type: "Vignette",
+        props: { offset: 0.3, darkness: 0.9 },
+        children: [],
+      },
+      fog: {
+        type: "Fog",
+        props: { color: "#030008", near: 5, far: 50 },
+        children: [],
+      },
+    },
+  },
+};

+ 29 - 0
examples/react-three-fiber/app/scenes/index.ts

@@ -0,0 +1,29 @@
+import type { Scene } from "./_helpers";
+import { hyperspaceTunnel } from "./hyperspace-tunnel";
+import { orbitalChaos } from "./orbital-chaos";
+import { clockworkOrrery } from "./clockwork-orrery";
+import { deepSeaAbyss } from "./deep-sea-abyss";
+import { stormCell } from "./storm-cell";
+import { perpetualMotion } from "./perpetual-motion";
+import { floatingIslands } from "./floating-islands";
+import { productShowroom } from "./product-showroom";
+import { portalGallery } from "./portal-gallery";
+import { pipes } from "./pipes";
+import { mystify } from "./mystify";
+import { starfield } from "./starfield";
+export type { Scene };
+
+export const scenes: Scene[] = [
+  hyperspaceTunnel,
+  orbitalChaos,
+  clockworkOrrery,
+  deepSeaAbyss,
+  stormCell,
+  perpetualMotion,
+  floatingIslands,
+  productShowroom,
+  portalGallery,
+  pipes,
+  mystify,
+  starfield,
+];

+ 400 - 0
examples/react-three-fiber/app/scenes/mystify.ts

@@ -0,0 +1,400 @@
+import type { Scene } from "./_helpers";
+
+export const mystify: Scene = {
+  name: "Mystify",
+  description:
+    "Neon wireframe torus knots orbiting in the void -- the Windows Mystify screensaver in 3D",
+  spec: {
+    root: "scene",
+    elements: {
+      scene: {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "cam",
+          "ambient",
+          "shape1",
+          "shape2",
+          "shape3",
+          "shape4",
+          "shape5",
+          "shape6",
+          "shape7",
+          "post",
+          "controls",
+        ],
+      },
+      cam: {
+        type: "PerspectiveCamera",
+        props: {
+          position: [0, 0, 10],
+          rotation: null,
+          scale: null,
+          fov: 55,
+          near: 0.1,
+          far: 1000,
+          makeDefault: true,
+        },
+        children: [],
+      },
+      ambient: {
+        type: "AmbientLight",
+        props: { color: "#111111", intensity: 0.1 },
+        children: [],
+      },
+      shape1: {
+        type: "Orbit",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 0.5,
+          radius: 3,
+          tilt: 0.3,
+        },
+        children: ["s1-spin"],
+      },
+      "s1-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 1.5,
+          axis: "x",
+        },
+        children: ["s1-body"],
+      },
+      "s1-body": {
+        type: "TorusKnot",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          radius: 0.4,
+          tube: 0.02,
+          p: 2,
+          q: 3,
+          tubularSegments: 128,
+          radialSegments: 4,
+          material: {
+            color: "#00ffff",
+            emissive: "#00ffff",
+            emissiveIntensity: 2,
+            wireframe: true,
+          },
+        },
+        children: [],
+      },
+      shape2: {
+        type: "Orbit",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: -0.4,
+          radius: 4,
+          tilt: -0.5,
+        },
+        children: ["s2-spin"],
+      },
+      "s2-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: -1,
+          axis: "z",
+        },
+        children: ["s2-body"],
+      },
+      "s2-body": {
+        type: "Torus",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          radius: 0.6,
+          tube: 0.015,
+          radialSegments: 4,
+          tubularSegments: 128,
+          material: {
+            color: "#ff00ff",
+            emissive: "#ff00ff",
+            emissiveIntensity: 2,
+            wireframe: true,
+          },
+        },
+        children: [],
+      },
+      shape3: {
+        type: "Orbit",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 0.7,
+          radius: 2.5,
+          tilt: 0.8,
+        },
+        children: ["s3-spin"],
+      },
+      "s3-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 2,
+          axis: "y",
+        },
+        children: ["s3-body"],
+      },
+      "s3-body": {
+        type: "TorusKnot",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          radius: 0.35,
+          tube: 0.015,
+          p: 3,
+          q: 2,
+          tubularSegments: 128,
+          radialSegments: 4,
+          material: {
+            color: "#ffff00",
+            emissive: "#ffff00",
+            emissiveIntensity: 2,
+            wireframe: true,
+          },
+        },
+        children: [],
+      },
+      shape4: {
+        type: "Orbit",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: -0.6,
+          radius: 3.5,
+          tilt: -0.2,
+        },
+        children: ["s4-spin"],
+      },
+      "s4-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 1.2,
+          axis: "x",
+        },
+        children: ["s4-body"],
+      },
+      "s4-body": {
+        type: "Torus",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          radius: 0.5,
+          tube: 0.012,
+          radialSegments: 4,
+          tubularSegments: 128,
+          material: {
+            color: "#00ff44",
+            emissive: "#00ff44",
+            emissiveIntensity: 2,
+            wireframe: true,
+          },
+        },
+        children: [],
+      },
+      shape5: {
+        type: "Orbit",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 0.3,
+          radius: 5,
+          tilt: 0.6,
+        },
+        children: ["s5-spin"],
+      },
+      "s5-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: -1.8,
+          axis: "z",
+        },
+        children: ["s5-body"],
+      },
+      "s5-body": {
+        type: "TorusKnot",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          radius: 0.5,
+          tube: 0.018,
+          p: 2,
+          q: 5,
+          tubularSegments: 128,
+          radialSegments: 4,
+          material: {
+            color: "#ff4400",
+            emissive: "#ff4400",
+            emissiveIntensity: 2,
+            wireframe: true,
+          },
+        },
+        children: [],
+      },
+      shape6: {
+        type: "Orbit",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: -0.8,
+          radius: 2,
+          tilt: -0.7,
+        },
+        children: ["s6-spin"],
+      },
+      "s6-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 2.5,
+          axis: "y",
+        },
+        children: ["s6-body"],
+      },
+      "s6-body": {
+        type: "Torus",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          radius: 0.3,
+          tube: 0.01,
+          radialSegments: 4,
+          tubularSegments: 128,
+          material: {
+            color: "#4488ff",
+            emissive: "#4488ff",
+            emissiveIntensity: 2,
+            wireframe: true,
+          },
+        },
+        children: [],
+      },
+      shape7: {
+        type: "Orbit",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 0.45,
+          radius: 4.5,
+          tilt: 1,
+        },
+        children: ["s7-spin"],
+      },
+      "s7-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: -0.8,
+          axis: "x",
+        },
+        children: ["s7-body"],
+      },
+      "s7-body": {
+        type: "TorusKnot",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          radius: 0.45,
+          tube: 0.015,
+          p: 3,
+          q: 4,
+          tubularSegments: 128,
+          radialSegments: 4,
+          material: {
+            color: "#ff88ff",
+            emissive: "#ff88ff",
+            emissiveIntensity: 2,
+            wireframe: true,
+          },
+        },
+        children: [],
+      },
+      post: {
+        type: "EffectComposer",
+        props: { enabled: true, multisampling: 8 },
+        children: ["bloom", "vignette"],
+      },
+      bloom: {
+        type: "Bloom",
+        props: {
+          intensity: 3,
+          luminanceThreshold: 0.05,
+          luminanceSmoothing: 0.3,
+          mipmapBlur: true,
+        },
+        children: [],
+      },
+      vignette: {
+        type: "Vignette",
+        props: { offset: 0.2, darkness: 0.8 },
+        children: [],
+      },
+      controls: {
+        type: "OrbitControls",
+        props: {
+          enableDamping: true,
+          dampingFactor: null,
+          enableZoom: true,
+          enablePan: true,
+          enableRotate: true,
+          minDistance: 4,
+          maxDistance: 18,
+          minPolarAngle: null,
+          maxPolarAngle: null,
+          autoRotate: true,
+          autoRotateSpeed: 0.3,
+        },
+        children: [],
+      },
+    },
+  },
+};

+ 554 - 0
examples/react-three-fiber/app/scenes/orbital-chaos.ts

@@ -0,0 +1,554 @@
+import { PI } from "./_helpers";
+import type { Scene } from "./_helpers";
+
+export const orbitalChaos: Scene = {
+  name: "Orbital Chaos",
+  description:
+    "Everything in motion -- orbiting glass, spinning knots, morphing core, all with bloom",
+  spec: {
+    root: "scene",
+    elements: {
+      scene: {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "cam",
+          "env",
+          "stars",
+          "ambient",
+          "core-light",
+          "accent-1",
+          "accent-2",
+          "core-pulse",
+          "orbit-1",
+          "orbit-2",
+          "orbit-3",
+          "orbit-4",
+          "orbit-5",
+          "orbit-6",
+          "ring-spin-fast",
+          "ring-spin-slow",
+          "sparkles",
+          "post",
+          "fog",
+          "controls",
+        ],
+      },
+      cam: {
+        type: "PerspectiveCamera",
+        props: {
+          position: [0, 2, 8],
+          fov: 50,
+          makeDefault: true,
+        },
+        children: [],
+      },
+      env: {
+        type: "Environment",
+        props: { preset: "night", background: false, blur: 1, intensity: 0.15 },
+        children: [],
+      },
+      stars: {
+        type: "Stars",
+        props: {
+          radius: 120,
+          depth: 60,
+          count: 8000,
+          factor: 5,
+          saturation: 0.2,
+          fade: true,
+          speed: 0.5,
+        },
+        children: [],
+      },
+      ambient: {
+        type: "AmbientLight",
+        props: { color: "#110022", intensity: 0.3 },
+        children: [],
+      },
+      "core-light": {
+        type: "PointLight",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          color: "#ff44aa",
+          intensity: 80,
+          distance: 25,
+          decay: 2,
+          castShadow: false,
+        },
+        children: [],
+      },
+      "accent-1": {
+        type: "PointLight",
+        props: {
+          position: [5, 3, 5],
+          rotation: null,
+          scale: null,
+          color: "#00ffff",
+          intensity: 25,
+          distance: 20,
+          decay: 2,
+          castShadow: false,
+        },
+        children: [],
+      },
+      "accent-2": {
+        type: "PointLight",
+        props: {
+          position: [-5, -2, -5],
+          rotation: null,
+          scale: null,
+          color: "#ff00ff",
+          intensity: 25,
+          distance: 20,
+          decay: 2,
+          castShadow: false,
+        },
+        children: [],
+      },
+
+      "core-pulse": {
+        type: "Pulse",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 0.8,
+          min: 0.85,
+          max: 1.15,
+        },
+        children: ["core-spin"],
+      },
+      "core-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 0.3,
+          axis: "y",
+        },
+        children: ["core-blob"],
+      },
+      "core-blob": {
+        type: "DistortSphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          radius: 1.2,
+          widthSegments: 64,
+          heightSegments: 32,
+          color: "#ff2288",
+          speed: 4,
+          distort: 0.6,
+          metalness: 0.1,
+          roughness: 0.1,
+        },
+        children: [],
+      },
+
+      "orbit-1": {
+        type: "Orbit",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          speed: 0.8,
+          radius: 4,
+          tilt: 0.5,
+        },
+        children: ["orb-1-spin"],
+      },
+      "orb-1-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 2,
+          axis: "x",
+        },
+        children: ["orb-1"],
+      },
+      "orb-1": {
+        type: "GlassSphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          radius: 0.4,
+          widthSegments: 32,
+          heightSegments: 16,
+          color: "#00ffff",
+          transmission: 1,
+          thickness: 0.5,
+          roughness: 0,
+          chromaticAberration: 0.15,
+          ior: 1.8,
+          distortion: 0.1,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+
+      "orbit-2": {
+        type: "Orbit",
+        props: {
+          position: [0, 0.5, 0],
+          rotation: null,
+          scale: null,
+          speed: -0.6,
+          radius: 5.5,
+          tilt: 1,
+        },
+        children: ["orb-2-spin"],
+      },
+      "orb-2-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 3,
+          axis: "z",
+        },
+        children: ["orb-2"],
+      },
+      "orb-2": {
+        type: "TorusKnot",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          material: {
+            color: "#ff00ff",
+            metalness: 0.9,
+            roughness: 0.1,
+            emissive: "#ff00ff",
+            emissiveIntensity: 0.8,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 0.35,
+          tube: 0.12,
+          tubularSegments: 64,
+          radialSegments: 8,
+          p: 2,
+          q: 3,
+        },
+        children: [],
+      },
+
+      "orbit-3": {
+        type: "Orbit",
+        props: {
+          position: [0, -0.3, 0],
+          rotation: null,
+          scale: null,
+          speed: 1.2,
+          radius: 3.2,
+          tilt: 0.3,
+        },
+        children: ["orb-3"],
+      },
+      "orb-3": {
+        type: "GlassSphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          radius: 0.3,
+          widthSegments: 32,
+          heightSegments: 16,
+          color: "#ff8800",
+          transmission: 1,
+          thickness: 0.4,
+          roughness: 0,
+          chromaticAberration: 0.1,
+          ior: 1.6,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+
+      "orbit-4": {
+        type: "Orbit",
+        props: {
+          position: [0, 1, 0],
+          rotation: null,
+          scale: null,
+          speed: -1.5,
+          radius: 6.5,
+          tilt: -0.8,
+        },
+        children: ["orb-4-spin"],
+      },
+      "orb-4-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: -4,
+          axis: "y",
+        },
+        children: ["orb-4"],
+      },
+      "orb-4": {
+        type: "Sphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          material: {
+            color: "#00ff88",
+            metalness: null,
+            roughness: null,
+            emissive: "#00ff88",
+            emissiveIntensity: 0.8,
+            opacity: null,
+            transparent: null,
+            wireframe: true,
+          },
+          radius: 0.35,
+          widthSegments: 16,
+          heightSegments: 12,
+        },
+        children: [],
+      },
+
+      "orbit-5": {
+        type: "Orbit",
+        props: {
+          position: [0, -0.5, 0],
+          rotation: null,
+          scale: null,
+          speed: 0.4,
+          radius: 8,
+          tilt: 0.2,
+        },
+        children: ["orb-5-spin"],
+      },
+      "orb-5-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 1.5,
+          axis: "z",
+        },
+        children: ["orb-5"],
+      },
+      "orb-5": {
+        type: "GlassBox",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          width: 0.5,
+          height: 0.5,
+          depth: 0.5,
+          color: "#ff44ff",
+          transmission: 1,
+          thickness: 0.4,
+          roughness: 0,
+          chromaticAberration: 0.08,
+          ior: 1.7,
+          distortion: 0,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+
+      "orbit-6": {
+        type: "Orbit",
+        props: {
+          position: [0, 0.8, 0],
+          rotation: null,
+          scale: null,
+          speed: 2,
+          radius: 2.2,
+          tilt: 1.5,
+        },
+        children: ["orb-6"],
+      },
+      "orb-6": {
+        type: "DistortSphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          radius: 0.25,
+          widthSegments: 32,
+          heightSegments: 16,
+          color: "#ffcc00",
+          speed: 5,
+          distort: 0.7,
+          metalness: 0.5,
+          roughness: 0.1,
+        },
+        children: [],
+      },
+
+      "ring-spin-fast": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: [0.3, 0, 0],
+          scale: null,
+          speed: 0.5,
+          axis: "y",
+        },
+        children: ["ring-inner"],
+      },
+      "ring-inner": {
+        type: "Torus",
+        props: {
+          position: null,
+          rotation: [PI / 2, 0, 0],
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          material: {
+            color: "#00ffff",
+            metalness: 0.9,
+            roughness: 0.1,
+            emissive: "#00ccff",
+            emissiveIntensity: 0.5,
+            opacity: 0.3,
+            transparent: true,
+            wireframe: null,
+          },
+          radius: 9,
+          tube: 0.015,
+          radialSegments: null,
+          tubularSegments: 128,
+        },
+        children: [],
+      },
+      "ring-spin-slow": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: [-0.5, 0, 0.3],
+          scale: null,
+          speed: -0.3,
+          axis: "y",
+        },
+        children: ["ring-outer"],
+      },
+      "ring-outer": {
+        type: "Torus",
+        props: {
+          position: null,
+          rotation: [PI / 2, 0, 0],
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          material: {
+            color: "#ff00ff",
+            metalness: 0.9,
+            roughness: 0.1,
+            emissive: "#ff00aa",
+            emissiveIntensity: 0.3,
+            opacity: 0.2,
+            transparent: true,
+            wireframe: null,
+          },
+          radius: 11,
+          tube: 0.012,
+          radialSegments: null,
+          tubularSegments: 128,
+        },
+        children: [],
+      },
+
+      sparkles: {
+        type: "Sparkles",
+        props: {
+          position: null,
+          rotation: null,
+          scale: [18, 18, 18],
+          count: 400,
+          speed: 0.4,
+          opacity: 0.5,
+          color: "#ffddaa",
+          size: 1.2,
+          noise: 2,
+        },
+        children: [],
+      },
+      post: {
+        type: "EffectComposer",
+        props: { enabled: true, multisampling: 8 },
+        children: ["bloom", "vignette"],
+      },
+      bloom: {
+        type: "Bloom",
+        props: {
+          intensity: 2,
+          luminanceThreshold: 0.15,
+          luminanceSmoothing: null,
+          mipmapBlur: true,
+        },
+        children: [],
+      },
+      vignette: {
+        type: "Vignette",
+        props: { offset: 0.4, darkness: 0.7 },
+        children: [],
+      },
+      fog: {
+        type: "Fog",
+        props: { color: "#030008", near: 10, far: 40 },
+        children: [],
+      },
+      controls: {
+        type: "OrbitControls",
+        props: {
+          enableDamping: true,
+          dampingFactor: null,
+          enableZoom: true,
+          enablePan: null,
+          enableRotate: true,
+          minDistance: 5,
+          maxDistance: 25,
+          minPolarAngle: null,
+          maxPolarAngle: null,
+          autoRotate: true,
+          autoRotateSpeed: 0.4,
+        },
+        children: [],
+      },
+    },
+  },
+};

+ 307 - 0
examples/react-three-fiber/app/scenes/perpetual-motion.ts

@@ -0,0 +1,307 @@
+import { PI } from "./_helpers";
+import type { Scene } from "./_helpers";
+
+export const perpetualMotion: Scene = {
+  name: "Perpetual Motion Machine",
+  description:
+    "A gyroscope/gimbal with 3 nested spinning rings on different axes, with a perfectly still glass sphere floating at the center. Clean, minimal, mesmerizing.",
+  spec: {
+    root: "scene",
+    elements: {
+      scene: {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "env",
+          "ambient",
+          "key",
+          "fill",
+          "outer-spin",
+          "sparkles",
+          "floor",
+          "shadows",
+          "post",
+          "controls",
+        ],
+      },
+      env: {
+        type: "Environment",
+        props: {
+          preset: "studio",
+          background: false,
+          blur: null,
+          intensity: 1,
+        },
+        children: [],
+      },
+      ambient: {
+        type: "AmbientLight",
+        props: { color: "#ffffff", intensity: 0.3 },
+        children: [],
+      },
+      key: {
+        type: "DirectionalLight",
+        props: {
+          position: [5, 8, 5],
+          rotation: null,
+          scale: null,
+          color: "#ffffff",
+          intensity: 2,
+          castShadow: true,
+        },
+        children: [],
+      },
+      fill: {
+        type: "PointLight",
+        props: {
+          position: [-3, 4, -3],
+          rotation: null,
+          scale: null,
+          color: "#aaccff",
+          intensity: 10,
+          distance: 15,
+          decay: 2,
+          castShadow: false,
+        },
+        children: [],
+      },
+      "outer-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 0.4,
+          axis: "y",
+        },
+        children: ["outer-ring", "mid-spin"],
+      },
+      "outer-ring": {
+        type: "Torus",
+        props: {
+          position: null,
+          rotation: [PI / 2, 0, 0],
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          material: {
+            color: "#cccccc",
+            metalness: 1,
+            roughness: 0.05,
+            emissive: "#ffffff",
+            emissiveIntensity: 0.05,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 4,
+          tube: 0.06,
+          radialSegments: null,
+          tubularSegments: 128,
+        },
+        children: [],
+      },
+      "mid-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: -0.8,
+          axis: "x",
+        },
+        children: ["mid-ring", "inner-spin"],
+      },
+      "mid-ring": {
+        type: "Torus",
+        props: {
+          position: null,
+          rotation: [PI / 2, 0, 0],
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          material: {
+            color: "#ffcc44",
+            metalness: 0.95,
+            roughness: 0.08,
+            emissive: "#cc9900",
+            emissiveIntensity: 0.1,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 3,
+          tube: 0.05,
+          radialSegments: null,
+          tubularSegments: 128,
+        },
+        children: [],
+      },
+      "inner-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 1.5,
+          axis: "z",
+        },
+        children: ["inner-ring", "center-float"],
+      },
+      "inner-ring": {
+        type: "Torus",
+        props: {
+          position: null,
+          rotation: [PI / 2, 0, 0],
+          scale: null,
+          castShadow: false,
+          receiveShadow: false,
+          material: {
+            color: "#cc8844",
+            metalness: 0.9,
+            roughness: 0.1,
+            emissive: "#996633",
+            emissiveIntensity: 0.1,
+            opacity: null,
+            transparent: null,
+            wireframe: null,
+          },
+          radius: 2,
+          tube: 0.04,
+          radialSegments: null,
+          tubularSegments: 128,
+        },
+        children: [],
+      },
+      "center-float": {
+        type: "Float",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          speed: 0.3,
+          rotationIntensity: 0,
+          floatIntensity: 0.1,
+          enabled: true,
+        },
+        children: ["center-orb"],
+      },
+      "center-orb": {
+        type: "GlassSphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: true,
+          receiveShadow: false,
+          radius: 0.8,
+          widthSegments: 64,
+          heightSegments: 32,
+          color: "#ffffff",
+          transmission: 1,
+          thickness: 1,
+          roughness: 0,
+          chromaticAberration: 0.12,
+          ior: 2,
+          distortion: 0.1,
+          distortionScale: 0.4,
+          temporalDistortion: 0.4,
+          samples: 10,
+          resolution: 256,
+        },
+        children: [],
+      },
+      sparkles: {
+        type: "Sparkles",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: [10, 10, 10],
+          count: 100,
+          speed: 0.1,
+          opacity: 0.2,
+          color: "#ccddff",
+          size: 0.8,
+          noise: 2,
+        },
+        children: [],
+      },
+      floor: {
+        type: "ReflectorPlane",
+        props: {
+          position: [0, -4.5, 0],
+          rotation: [-PI / 2, 0, 0],
+          scale: null,
+          width: 25,
+          height: 25,
+          color: "#1a1a2a",
+          resolution: 1024,
+          blur: 400,
+          mirror: 0.7,
+          mixBlur: 10,
+          mixStrength: 2,
+          depthScale: 0.2,
+          metalness: 0.5,
+          roughness: 1,
+        },
+        children: [],
+      },
+      shadows: {
+        type: "ContactShadows",
+        props: {
+          position: [0, -4.5, 0],
+          rotation: null,
+          scale: null,
+          opacity: 0.3,
+          width: 12,
+          height: 12,
+          blur: 2.5,
+          near: null,
+          far: 10,
+          smooth: true,
+          resolution: 512,
+          frames: null,
+          color: "#000000",
+        },
+        children: [],
+      },
+      post: {
+        type: "EffectComposer",
+        props: { enabled: true, multisampling: 8 },
+        children: ["bloom", "vignette"],
+      },
+      bloom: {
+        type: "Bloom",
+        props: {
+          intensity: 0.8,
+          luminanceThreshold: 0.25,
+          luminanceSmoothing: null,
+          mipmapBlur: true,
+        },
+        children: [],
+      },
+      vignette: {
+        type: "Vignette",
+        props: { offset: 0.5, darkness: 0.4 },
+        children: [],
+      },
+      controls: {
+        type: "OrbitControls",
+        props: {
+          enableDamping: true,
+          dampingFactor: null,
+          enableZoom: true,
+          enablePan: null,
+          enableRotate: true,
+          minDistance: 5,
+          maxDistance: 16,
+          minPolarAngle: 0.3,
+          maxPolarAngle: 1.5,
+          autoRotate: true,
+          autoRotateSpeed: 0.3,
+        },
+        children: [],
+      },
+    },
+  },
+};

+ 764 - 0
examples/react-three-fiber/app/scenes/pipes.ts

@@ -0,0 +1,764 @@
+import type { Scene } from "./_helpers";
+
+const goldMaterial = {
+  color: "#cc8833",
+  metalness: 1,
+  roughness: 0.05,
+};
+
+const blueMaterial = {
+  color: "#3388cc",
+  metalness: 1,
+  roughness: 0.05,
+};
+
+const magentaMaterial = {
+  color: "#cc3388",
+  metalness: 1,
+  roughness: 0.05,
+};
+
+export const pipes: Scene = {
+  name: "Pipes",
+  description:
+    "Classic 3D Pipes screensaver -- chrome pipes snaking through space at right angles",
+  spec: {
+    root: "scene",
+    elements: {
+      scene: {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "camera",
+          "env",
+          "ambient",
+          "directional",
+          "pipe-system",
+          "post",
+          "fog",
+          "controls",
+        ],
+      },
+      camera: {
+        type: "PerspectiveCamera",
+        props: {
+          position: [0, 3, 12],
+          rotation: null,
+          scale: null,
+          fov: 50,
+          near: null,
+          far: null,
+          makeDefault: true,
+        },
+        children: [],
+      },
+      env: {
+        type: "Environment",
+        props: {
+          preset: "warehouse",
+          background: false,
+          blur: 0.5,
+          intensity: 1,
+        },
+        children: [],
+      },
+      ambient: {
+        type: "AmbientLight",
+        props: { color: "#ffffff", intensity: 0.3 },
+        children: [],
+      },
+      directional: {
+        type: "DirectionalLight",
+        props: {
+          position: [5, 10, 5],
+          rotation: null,
+          scale: null,
+          color: "#ffffff",
+          intensity: 1,
+          castShadow: null,
+        },
+        children: [],
+      },
+      "pipe-system": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 0.1,
+          axis: "y",
+        },
+        children: ["pipe1-group", "pipe2-group", "pipe3-group"],
+      },
+      "pipe1-group": {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "p1-j0",
+          "p1-s1",
+          "p1-j1",
+          "p1-s2",
+          "p1-j2",
+          "p1-s3",
+          "p1-j3",
+          "p1-s4",
+          "p1-j4",
+          "p1-s5",
+          "p1-j5",
+          "p1-s6",
+          "p1-j6",
+        ],
+      },
+      "p1-j0": {
+        type: "Sphere",
+        props: {
+          position: [-3, -3, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: goldMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p1-j1": {
+        type: "Sphere",
+        props: {
+          position: [-3, 0, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: goldMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p1-j2": {
+        type: "Sphere",
+        props: {
+          position: [1, 0, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: goldMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p1-j3": {
+        type: "Sphere",
+        props: {
+          position: [1, 2, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: goldMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p1-j4": {
+        type: "Sphere",
+        props: {
+          position: [1, 2, -3],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: goldMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p1-j5": {
+        type: "Sphere",
+        props: {
+          position: [1, 0, -3],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: goldMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p1-j6": {
+        type: "Sphere",
+        props: {
+          position: [-1, 0, -3],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: goldMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p1-s1": {
+        type: "Cylinder",
+        props: {
+          position: [-3, -1.5, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: goldMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 3,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "p1-s2": {
+        type: "Cylinder",
+        props: {
+          position: [-1, 0, 0],
+          rotation: [0, 0, 1.5708],
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: goldMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 4,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "p1-s3": {
+        type: "Cylinder",
+        props: {
+          position: [1, 1, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: goldMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 2,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "p1-s4": {
+        type: "Cylinder",
+        props: {
+          position: [1, 2, -1.5],
+          rotation: [1.5708, 0, 0],
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: goldMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 3,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "p1-s5": {
+        type: "Cylinder",
+        props: {
+          position: [1, 1, -3],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: goldMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 2,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "p1-s6": {
+        type: "Cylinder",
+        props: {
+          position: [0, 0, -3],
+          rotation: [0, 0, 1.5708],
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: goldMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 2,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "pipe2-group": {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "p2-j0",
+          "p2-s1",
+          "p2-j1",
+          "p2-s2",
+          "p2-j2",
+          "p2-s3",
+          "p2-j3",
+          "p2-s4",
+          "p2-j4",
+          "p2-s5",
+          "p2-j5",
+          "p2-s6",
+          "p2-j6",
+        ],
+      },
+      "p2-j0": {
+        type: "Sphere",
+        props: {
+          position: [2, -3, 2],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: blueMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p2-j1": {
+        type: "Sphere",
+        props: {
+          position: [2, 1, 2],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: blueMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p2-j2": {
+        type: "Sphere",
+        props: {
+          position: [2, 1, -2],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: blueMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p2-j3": {
+        type: "Sphere",
+        props: {
+          position: [-1, 1, -2],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: blueMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p2-j4": {
+        type: "Sphere",
+        props: {
+          position: [-1, 4, -2],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: blueMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p2-j5": {
+        type: "Sphere",
+        props: {
+          position: [-3, 4, -2],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: blueMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p2-j6": {
+        type: "Sphere",
+        props: {
+          position: [-3, 4, 1],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: blueMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p2-s1": {
+        type: "Cylinder",
+        props: {
+          position: [2, -1, 2],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: blueMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 4,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "p2-s2": {
+        type: "Cylinder",
+        props: {
+          position: [2, 1, 0],
+          rotation: [1.5708, 0, 0],
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: blueMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 4,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "p2-s3": {
+        type: "Cylinder",
+        props: {
+          position: [0.5, 1, -2],
+          rotation: [0, 0, 1.5708],
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: blueMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 3,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "p2-s4": {
+        type: "Cylinder",
+        props: {
+          position: [-1, 2.5, -2],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: blueMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 3,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "p2-s5": {
+        type: "Cylinder",
+        props: {
+          position: [-2, 4, -2],
+          rotation: [0, 0, 1.5708],
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: blueMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 2,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "p2-s6": {
+        type: "Cylinder",
+        props: {
+          position: [-3, 4, -0.5],
+          rotation: [1.5708, 0, 0],
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: blueMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 3,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "pipe3-group": {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "p3-j0",
+          "p3-s1",
+          "p3-j1",
+          "p3-s2",
+          "p3-j2",
+          "p3-s3",
+          "p3-j3",
+          "p3-s4",
+          "p3-j4",
+          "p3-s5",
+          "p3-j5",
+        ],
+      },
+      "p3-j0": {
+        type: "Sphere",
+        props: {
+          position: [-2, 3, 3],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: magentaMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p3-j1": {
+        type: "Sphere",
+        props: {
+          position: [-2, -2, 3],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: magentaMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p3-j2": {
+        type: "Sphere",
+        props: {
+          position: [1, -2, 3],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: magentaMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p3-j3": {
+        type: "Sphere",
+        props: {
+          position: [1, -2, -2],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: magentaMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p3-j4": {
+        type: "Sphere",
+        props: {
+          position: [1, 0, -2],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: magentaMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p3-j5": {
+        type: "Sphere",
+        props: {
+          position: [-3, 0, -2],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: magentaMaterial,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+        },
+        children: [],
+      },
+      "p3-s1": {
+        type: "Cylinder",
+        props: {
+          position: [-2, 0.5, 3],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: magentaMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 5,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "p3-s2": {
+        type: "Cylinder",
+        props: {
+          position: [-0.5, -2, 3],
+          rotation: [0, 0, 1.5708],
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: magentaMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 3,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "p3-s3": {
+        type: "Cylinder",
+        props: {
+          position: [1, -2, 0.5],
+          rotation: [1.5708, 0, 0],
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: magentaMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 5,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "p3-s4": {
+        type: "Cylinder",
+        props: {
+          position: [1, -1, -2],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: magentaMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 2,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      "p3-s5": {
+        type: "Cylinder",
+        props: {
+          position: [-1, 0, -2],
+          rotation: [0, 0, 1.5708],
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          material: magentaMaterial,
+          radiusTop: 0.08,
+          radiusBottom: 0.08,
+          height: 4,
+          radialSegments: 12,
+        },
+        children: [],
+      },
+      post: {
+        type: "EffectComposer",
+        props: { enabled: true, multisampling: 8 },
+        children: ["bloom", "vignette"],
+      },
+      bloom: {
+        type: "Bloom",
+        props: {
+          intensity: 0.4,
+          luminanceThreshold: 0.4,
+          luminanceSmoothing: null,
+          mipmapBlur: true,
+        },
+        children: [],
+      },
+      vignette: {
+        type: "Vignette",
+        props: { offset: 0.3, darkness: 0.4 },
+        children: [],
+      },
+      fog: {
+        type: "Fog",
+        props: { color: "#111111", near: 10, far: 30 },
+        children: [],
+      },
+      controls: {
+        type: "OrbitControls",
+        props: {
+          enableDamping: true,
+          dampingFactor: null,
+          enableZoom: null,
+          enablePan: null,
+          enableRotate: null,
+          minDistance: 5,
+          maxDistance: 20,
+          minPolarAngle: null,
+          maxPolarAngle: null,
+          autoRotate: true,
+          autoRotateSpeed: 0.6,
+        },
+        children: [],
+      },
+    },
+  },
+};

+ 809 - 0
examples/react-three-fiber/app/scenes/portal-gallery.ts

@@ -0,0 +1,809 @@
+import type { Scene } from "./_helpers";
+
+export const portalGallery: Scene = {
+  name: "Portal Gallery",
+  description:
+    "Three framed portals into different worlds -- sunset clouds, starfield, and enchanted forest",
+  spec: {
+    root: "scene",
+    elements: {
+      scene: {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "cam",
+          "env",
+          "ambient",
+          "directional",
+          "floor",
+          "shadows",
+          "frame1",
+          "frame2",
+          "frame3",
+          "gallery-sparkles",
+          "post",
+          "controls",
+        ],
+      },
+      cam: {
+        type: "PerspectiveCamera",
+        props: {
+          position: [0, 2, 7],
+          rotation: null,
+          scale: null,
+          fov: 50,
+          near: 0.1,
+          far: 200,
+          makeDefault: true,
+        },
+        children: [],
+      },
+      env: {
+        type: "Environment",
+        props: {
+          preset: "lobby",
+          background: false,
+          blur: 0,
+          intensity: 0.4,
+        },
+        children: [],
+      },
+      ambient: {
+        type: "AmbientLight",
+        props: { color: "#e8e0d8", intensity: 0.5 },
+        children: [],
+      },
+      directional: {
+        type: "DirectionalLight",
+        props: {
+          position: [5, 8, 3],
+          rotation: null,
+          scale: null,
+          color: "#ffffff",
+          intensity: 1,
+          castShadow: true,
+        },
+        children: [],
+      },
+      floor: {
+        type: "ReflectorPlane",
+        props: {
+          position: [0, 0, 0],
+          rotation: [-1.5708, 0, 0],
+          scale: null,
+          width: 30,
+          height: 30,
+          color: "#2a2a2a",
+          resolution: 1024,
+          blur: 400,
+          mirror: 0.4,
+          mixBlur: 10,
+          mixStrength: 1.5,
+          metalness: 0.5,
+          roughness: 0.8,
+        },
+        children: [],
+      },
+      shadows: {
+        type: "ContactShadows",
+        props: {
+          position: [0, 0.01, 0],
+          rotation: null,
+          scale: null,
+          opacity: 0.4,
+          width: 20,
+          height: 20,
+          blur: 2.5,
+          far: 8,
+          color: "#000000",
+        },
+        children: [],
+      },
+
+      // --- PORTAL 1 (left, sunset world) ---
+      frame1: {
+        type: "Group",
+        props: {
+          position: [-3.5, 2, -2],
+          rotation: [0, 0.3, 0],
+          scale: null,
+        },
+        children: [
+          "frame1-top",
+          "frame1-bottom",
+          "frame1-left",
+          "frame1-right",
+          "portal1-mesh",
+        ],
+      },
+      "frame1-top": {
+        type: "Box",
+        props: {
+          position: [0, 1.55, 0],
+          rotation: null,
+          scale: null,
+          width: 2.3,
+          height: 0.15,
+          depth: 0.15,
+          material: { color: "#8B7355", metalness: 0.3, roughness: 0.6 },
+        },
+        children: [],
+      },
+      "frame1-bottom": {
+        type: "Box",
+        props: {
+          position: [0, -1.55, 0],
+          rotation: null,
+          scale: null,
+          width: 2.3,
+          height: 0.15,
+          depth: 0.15,
+          material: { color: "#8B7355", metalness: 0.3, roughness: 0.6 },
+        },
+        children: [],
+      },
+      "frame1-left": {
+        type: "Box",
+        props: {
+          position: [-1.075, 0, 0],
+          rotation: null,
+          scale: null,
+          width: 0.15,
+          height: 3.25,
+          depth: 0.15,
+          material: { color: "#8B7355", metalness: 0.3, roughness: 0.6 },
+        },
+        children: [],
+      },
+      "frame1-right": {
+        type: "Box",
+        props: {
+          position: [1.075, 0, 0],
+          rotation: null,
+          scale: null,
+          width: 0.15,
+          height: 3.25,
+          depth: 0.15,
+          material: { color: "#8B7355", metalness: 0.3, roughness: 0.6 },
+        },
+        children: [],
+      },
+      "portal1-mesh": {
+        type: "Plane",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          width: 2,
+          height: 3,
+        },
+        children: ["portal1-mat"],
+      },
+      "portal1-mat": {
+        type: "MeshPortalMaterial",
+        props: { blend: 0, blur: 0.5, resolution: 512 },
+        children: [
+          "p1-sky",
+          "p1-sun-light",
+          "p1-cloud1",
+          "p1-cloud2",
+          "p1-cloud3",
+          "p1-ground",
+        ],
+      },
+      "p1-sky": {
+        type: "Sky",
+        props: {
+          distance: null,
+          sunPosition: [100, 10, 50],
+          inclination: null,
+          azimuth: null,
+          rayleigh: 2,
+          turbidity: 10,
+          mieCoefficient: 0.005,
+          mieDirectionalG: null,
+        },
+        children: [],
+      },
+      "p1-sun-light": {
+        type: "DirectionalLight",
+        props: {
+          position: [5, 5, 3],
+          rotation: null,
+          scale: null,
+          color: "#ffaa55",
+          intensity: 2,
+          castShadow: false,
+        },
+        children: [],
+      },
+      "p1-cloud1": {
+        type: "Cloud",
+        props: {
+          position: [-3, 4, -5],
+          rotation: null,
+          scale: null,
+          seed: 1,
+          segments: 20,
+          bounds: [6, 2, 3],
+          opacity: 0.6,
+          color: "#ffddaa",
+          speed: 0.1,
+          volume: 5,
+          fade: null,
+          growth: null,
+        },
+        children: [],
+      },
+      "p1-cloud2": {
+        type: "Cloud",
+        props: {
+          position: [4, 5, -8],
+          rotation: null,
+          scale: null,
+          seed: 7,
+          segments: 25,
+          bounds: [8, 2, 4],
+          opacity: 0.5,
+          color: "#ffccaa",
+          speed: 0.08,
+          volume: 6,
+          fade: null,
+          growth: null,
+        },
+        children: [],
+      },
+      "p1-cloud3": {
+        type: "Cloud",
+        props: {
+          position: [0, 3.5, -4],
+          rotation: null,
+          scale: null,
+          seed: 14,
+          segments: 15,
+          bounds: [5, 1.5, 2.5],
+          opacity: 0.7,
+          color: "#ffeedd",
+          speed: 0.12,
+          volume: 4,
+          fade: null,
+          growth: null,
+        },
+        children: [],
+      },
+      "p1-ground": {
+        type: "Plane",
+        props: {
+          position: [0, -2, 0],
+          rotation: [-1.5708, 0, 0],
+          scale: null,
+          width: 30,
+          height: 30,
+          material: { color: "#886644", roughness: 1 },
+        },
+        children: [],
+      },
+
+      // --- PORTAL 2 (center, night/stars world) ---
+      frame2: {
+        type: "Group",
+        props: {
+          position: [0, 2, -3],
+          rotation: null,
+          scale: null,
+        },
+        children: [
+          "frame2-top",
+          "frame2-bottom",
+          "frame2-left",
+          "frame2-right",
+          "portal2-mesh",
+        ],
+      },
+      "frame2-top": {
+        type: "Box",
+        props: {
+          position: [0, 1.55, 0],
+          rotation: null,
+          scale: null,
+          width: 2.3,
+          height: 0.15,
+          depth: 0.15,
+          material: { color: "#555566", metalness: 0.8, roughness: 0.2 },
+        },
+        children: [],
+      },
+      "frame2-bottom": {
+        type: "Box",
+        props: {
+          position: [0, -1.55, 0],
+          rotation: null,
+          scale: null,
+          width: 2.3,
+          height: 0.15,
+          depth: 0.15,
+          material: { color: "#555566", metalness: 0.8, roughness: 0.2 },
+        },
+        children: [],
+      },
+      "frame2-left": {
+        type: "Box",
+        props: {
+          position: [-1.075, 0, 0],
+          rotation: null,
+          scale: null,
+          width: 0.15,
+          height: 3.25,
+          depth: 0.15,
+          material: { color: "#555566", metalness: 0.8, roughness: 0.2 },
+        },
+        children: [],
+      },
+      "frame2-right": {
+        type: "Box",
+        props: {
+          position: [1.075, 0, 0],
+          rotation: null,
+          scale: null,
+          width: 0.15,
+          height: 3.25,
+          depth: 0.15,
+          material: { color: "#555566", metalness: 0.8, roughness: 0.2 },
+        },
+        children: [],
+      },
+      "portal2-mesh": {
+        type: "Plane",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          width: 2,
+          height: 3,
+        },
+        children: ["portal2-mat"],
+      },
+      "portal2-mat": {
+        type: "MeshPortalMaterial",
+        props: { blend: 0, blur: 0.5, resolution: 512 },
+        children: [
+          "p2-stars",
+          "p2-ambient",
+          "p2-nebula1",
+          "p2-nebula2",
+          "p2-sparkles",
+        ],
+      },
+      "p2-stars": {
+        type: "Stars",
+        props: {
+          radius: 80,
+          depth: 40,
+          count: 6000,
+          factor: 5,
+          saturation: 0.5,
+          fade: true,
+          speed: 0.3,
+        },
+        children: [],
+      },
+      "p2-ambient": {
+        type: "AmbientLight",
+        props: { color: "#111133", intensity: 0.3 },
+        children: [],
+      },
+      "p2-nebula1": {
+        type: "Sphere",
+        props: {
+          position: [3, 2, -8],
+          rotation: null,
+          scale: null,
+          radius: 2,
+          widthSegments: null,
+          heightSegments: null,
+          material: {
+            color: "#4400aa",
+            emissive: "#4400aa",
+            emissiveIntensity: 0.5,
+            transparent: true,
+            opacity: 0.3,
+          },
+        },
+        children: [],
+      },
+      "p2-nebula2": {
+        type: "Sphere",
+        props: {
+          position: [-4, -1, -10],
+          rotation: null,
+          scale: null,
+          radius: 3,
+          widthSegments: null,
+          heightSegments: null,
+          material: {
+            color: "#aa0044",
+            emissive: "#aa0044",
+            emissiveIntensity: 0.4,
+            transparent: true,
+            opacity: 0.25,
+          },
+        },
+        children: [],
+      },
+      "p2-sparkles": {
+        type: "Sparkles",
+        props: {
+          position: [0, 0, -5],
+          rotation: null,
+          scale: [10, 10, 10],
+          count: 200,
+          speed: 0.2,
+          opacity: 0.8,
+          color: "#aabbff",
+          size: 2,
+          noise: 3,
+        },
+        children: [],
+      },
+
+      // --- PORTAL 3 (right, forest/nature world) ---
+      frame3: {
+        type: "Group",
+        props: {
+          position: [3.5, 2, -2],
+          rotation: [0, -0.3, 0],
+          scale: null,
+        },
+        children: [
+          "frame3-top",
+          "frame3-bottom",
+          "frame3-left",
+          "frame3-right",
+          "portal3-mesh",
+        ],
+      },
+      "frame3-top": {
+        type: "Box",
+        props: {
+          position: [0, 1.55, 0],
+          rotation: null,
+          scale: null,
+          width: 2.3,
+          height: 0.15,
+          depth: 0.15,
+          material: { color: "#6B4423", metalness: 0.1, roughness: 0.8 },
+        },
+        children: [],
+      },
+      "frame3-bottom": {
+        type: "Box",
+        props: {
+          position: [0, -1.55, 0],
+          rotation: null,
+          scale: null,
+          width: 2.3,
+          height: 0.15,
+          depth: 0.15,
+          material: { color: "#6B4423", metalness: 0.1, roughness: 0.8 },
+        },
+        children: [],
+      },
+      "frame3-left": {
+        type: "Box",
+        props: {
+          position: [-1.075, 0, 0],
+          rotation: null,
+          scale: null,
+          width: 0.15,
+          height: 3.25,
+          depth: 0.15,
+          material: { color: "#6B4423", metalness: 0.1, roughness: 0.8 },
+        },
+        children: [],
+      },
+      "frame3-right": {
+        type: "Box",
+        props: {
+          position: [1.075, 0, 0],
+          rotation: null,
+          scale: null,
+          width: 0.15,
+          height: 3.25,
+          depth: 0.15,
+          material: { color: "#6B4423", metalness: 0.1, roughness: 0.8 },
+        },
+        children: [],
+      },
+      "portal3-mesh": {
+        type: "Plane",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          width: 2,
+          height: 3,
+        },
+        children: ["portal3-mat"],
+      },
+      "portal3-mat": {
+        type: "MeshPortalMaterial",
+        props: { blend: 0, blur: 0.5, resolution: 512 },
+        children: [
+          "p3-env",
+          "p3-ambient",
+          "p3-sun",
+          "p3-tree1",
+          "p3-tree2",
+          "p3-tree3",
+          "p3-tree4",
+          "p3-tree5",
+          "p3-ground",
+          "p3-fireflies",
+        ],
+      },
+      "p3-env": {
+        type: "Environment",
+        props: { preset: "forest", background: false, blur: 0, intensity: 0.8 },
+        children: [],
+      },
+      "p3-ambient": {
+        type: "AmbientLight",
+        props: { color: "#334422", intensity: 0.4 },
+        children: [],
+      },
+      "p3-sun": {
+        type: "DirectionalLight",
+        props: {
+          position: [-3, 8, 2],
+          rotation: null,
+          scale: null,
+          color: "#aaffaa",
+          intensity: 1.5,
+          castShadow: true,
+        },
+        children: [],
+      },
+      "p3-tree1": {
+        type: "Group",
+        props: { position: [-2, -2, -4], rotation: null, scale: null },
+        children: ["p3-t1-trunk", "p3-t1-leaves"],
+      },
+      "p3-t1-trunk": {
+        type: "Cylinder",
+        props: {
+          position: [0, 0.6, 0],
+          rotation: null,
+          scale: null,
+          radiusTop: 0.08,
+          radiusBottom: 0.12,
+          height: 1.2,
+          radialSegments: 8,
+          material: { color: "#4a3520", roughness: 0.9 },
+        },
+        children: [],
+      },
+      "p3-t1-leaves": {
+        type: "Cone",
+        props: {
+          position: [0, 1.8, 0],
+          rotation: null,
+          scale: null,
+          radius: 0.6,
+          height: 1.5,
+          radialSegments: 8,
+          material: { color: "#2d5a1e", roughness: 0.8 },
+        },
+        children: [],
+      },
+      "p3-tree2": {
+        type: "Group",
+        props: { position: [1.5, -2, -6], rotation: null, scale: null },
+        children: ["p3-t2-trunk", "p3-t2-leaves"],
+      },
+      "p3-t2-trunk": {
+        type: "Cylinder",
+        props: {
+          position: [0, 0.8, 0],
+          rotation: null,
+          scale: null,
+          radiusTop: 0.1,
+          radiusBottom: 0.15,
+          height: 1.6,
+          radialSegments: 8,
+          material: { color: "#3d2b15", roughness: 0.9 },
+        },
+        children: [],
+      },
+      "p3-t2-leaves": {
+        type: "Cone",
+        props: {
+          position: [0, 2.3, 0],
+          rotation: null,
+          scale: null,
+          radius: 0.8,
+          height: 2,
+          radialSegments: 8,
+          material: { color: "#1e4a10", roughness: 0.8 },
+        },
+        children: [],
+      },
+      "p3-tree3": {
+        type: "Group",
+        props: { position: [-0.5, -2, -3], rotation: null, scale: null },
+        children: ["p3-t3-trunk", "p3-t3-leaves"],
+      },
+      "p3-t3-trunk": {
+        type: "Cylinder",
+        props: {
+          position: [0, 0.5, 0],
+          rotation: null,
+          scale: null,
+          radiusTop: 0.06,
+          radiusBottom: 0.1,
+          height: 1,
+          radialSegments: 8,
+          material: { color: "#4a3520", roughness: 0.9 },
+        },
+        children: [],
+      },
+      "p3-t3-leaves": {
+        type: "Cone",
+        props: {
+          position: [0, 1.5, 0],
+          rotation: null,
+          scale: null,
+          radius: 0.5,
+          height: 1.2,
+          radialSegments: 8,
+          material: { color: "#3a6b2a", roughness: 0.8 },
+        },
+        children: [],
+      },
+      "p3-tree4": {
+        type: "Group",
+        props: { position: [3, -2, -8], rotation: null, scale: null },
+        children: ["p3-t4-trunk", "p3-t4-leaves"],
+      },
+      "p3-t4-trunk": {
+        type: "Cylinder",
+        props: {
+          position: [0, 1, 0],
+          rotation: null,
+          scale: null,
+          radiusTop: 0.12,
+          radiusBottom: 0.18,
+          height: 2,
+          radialSegments: 8,
+          material: { color: "#3d2b15", roughness: 0.9 },
+        },
+        children: [],
+      },
+      "p3-t4-leaves": {
+        type: "Cone",
+        props: {
+          position: [0, 2.8, 0],
+          rotation: null,
+          scale: null,
+          radius: 1,
+          height: 2.5,
+          radialSegments: 8,
+          material: { color: "#2d5a1e", roughness: 0.8 },
+        },
+        children: [],
+      },
+      "p3-tree5": {
+        type: "Group",
+        props: { position: [-3.5, -2, -7], rotation: null, scale: null },
+        children: ["p3-t5-trunk", "p3-t5-leaves"],
+      },
+      "p3-t5-trunk": {
+        type: "Cylinder",
+        props: {
+          position: [0, 0.7, 0],
+          rotation: null,
+          scale: null,
+          radiusTop: 0.09,
+          radiusBottom: 0.13,
+          height: 1.4,
+          radialSegments: 8,
+          material: { color: "#4a3520", roughness: 0.9 },
+        },
+        children: [],
+      },
+      "p3-t5-leaves": {
+        type: "Cone",
+        props: {
+          position: [0, 2, 0],
+          rotation: null,
+          scale: null,
+          radius: 0.7,
+          height: 1.8,
+          radialSegments: 8,
+          material: { color: "#1e4a10", roughness: 0.8 },
+        },
+        children: [],
+      },
+      "p3-ground": {
+        type: "Plane",
+        props: {
+          position: [0, -2, 0],
+          rotation: [-1.5708, 0, 0],
+          scale: null,
+          width: 30,
+          height: 30,
+          material: { color: "#3a5a2a", roughness: 1 },
+        },
+        children: [],
+      },
+      "p3-fireflies": {
+        type: "Sparkles",
+        props: {
+          position: [0, 0, -5],
+          rotation: null,
+          scale: [6, 4, 6],
+          count: 80,
+          speed: 0.3,
+          opacity: 0.7,
+          color: "#aaff66",
+          size: 1.5,
+          noise: 2,
+        },
+        children: [],
+      },
+
+      "gallery-sparkles": {
+        type: "Sparkles",
+        props: {
+          position: [0, 3, 0],
+          rotation: null,
+          scale: [12, 5, 10],
+          count: 40,
+          speed: 0.05,
+          opacity: 0.2,
+          color: "#ffffff",
+          size: 0.3,
+          noise: null,
+        },
+        children: [],
+      },
+      post: {
+        type: "EffectComposer",
+        props: { enabled: true, multisampling: 8 },
+        children: ["bloom", "vignette"],
+      },
+      bloom: {
+        type: "Bloom",
+        props: {
+          intensity: 0.6,
+          luminanceThreshold: 0.3,
+          luminanceSmoothing: null,
+          mipmapBlur: true,
+        },
+        children: [],
+      },
+      vignette: {
+        type: "Vignette",
+        props: { offset: 0.4, darkness: 0.4 },
+        children: [],
+      },
+      controls: {
+        type: "OrbitControls",
+        props: {
+          enableDamping: true,
+          dampingFactor: null,
+          enableZoom: true,
+          enablePan: null,
+          enableRotate: true,
+          minDistance: 4,
+          maxDistance: 16,
+          minPolarAngle: null,
+          maxPolarAngle: null,
+          autoRotate: true,
+          autoRotateSpeed: 0.3,
+          target: [0, 1.8, -2],
+        },
+        children: [],
+      },
+    },
+  },
+};

+ 455 - 0
examples/react-three-fiber/app/scenes/product-showroom.ts

@@ -0,0 +1,455 @@
+import type { Scene } from "./_helpers";
+
+export const productShowroom: Scene = {
+  name: "Product Showroom",
+  description:
+    "Sleek glass torus knot on a pedestal with studio lighting, reflections, and contact shadows",
+  spec: {
+    root: "scene",
+    elements: {
+      scene: {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "camera",
+          "env",
+          "backdrop",
+          "ambient",
+          "key-light",
+          "fill-light",
+          "rim-light",
+          "accent-light",
+          "pedestal",
+          "hero-float",
+          "accent1",
+          "accent2",
+          "accent3",
+          "floor",
+          "shadows",
+          "sparkles",
+          "fx",
+          "controls",
+        ],
+      },
+      camera: {
+        type: "PerspectiveCamera",
+        props: {
+          position: [0, 2, 6],
+          rotation: null,
+          scale: null,
+          fov: 40,
+          near: null,
+          far: null,
+          makeDefault: true,
+        },
+        children: [],
+      },
+      env: {
+        type: "Environment",
+        props: {
+          preset: "studio",
+          background: false,
+          blur: 0.5,
+          intensity: 0.8,
+        },
+        children: [],
+      },
+      backdrop: {
+        type: "Backdrop",
+        props: {
+          position: [0, -0.5, -3],
+          rotation: null,
+          scale: [25, 10, 8],
+          floor: 0.3,
+          segments: 30,
+          receiveShadow: true,
+        },
+        children: [],
+      },
+      ambient: {
+        type: "AmbientLight",
+        props: { color: "#e8e0f0", intensity: 0.25 },
+        children: [],
+      },
+      "key-light": {
+        type: "SpotLight",
+        props: {
+          position: [5, 8, 3],
+          rotation: null,
+          scale: null,
+          color: "#ffffff",
+          intensity: 80,
+          distance: 25,
+          decay: null,
+          angle: 0.5,
+          penumbra: 0.8,
+          castShadow: true,
+        },
+        children: [],
+      },
+      "fill-light": {
+        type: "SpotLight",
+        props: {
+          position: [-4, 5, 2],
+          rotation: null,
+          scale: null,
+          color: "#aaccff",
+          intensity: 30,
+          distance: 20,
+          decay: null,
+          angle: 0.6,
+          penumbra: 1,
+          castShadow: false,
+        },
+        children: [],
+      },
+      "rim-light": {
+        type: "SpotLight",
+        props: {
+          position: [0, 3, -5],
+          rotation: null,
+          scale: null,
+          color: "#ffddcc",
+          intensity: 40,
+          distance: 15,
+          decay: null,
+          angle: 0.4,
+          penumbra: 0.5,
+          castShadow: false,
+        },
+        children: [],
+      },
+      "accent-light": {
+        type: "PointLight",
+        props: {
+          position: [0, 0.5, 0],
+          rotation: null,
+          scale: null,
+          color: "#ffffff",
+          intensity: 5,
+          distance: 4,
+          decay: null,
+          castShadow: null,
+        },
+        children: [],
+      },
+      pedestal: {
+        type: "Group",
+        props: { position: [0, 0, 0], rotation: null, scale: null },
+        children: ["ped-base", "ped-column", "ped-top"],
+      },
+      "ped-base": {
+        type: "Cylinder",
+        props: {
+          position: [0, 0.05, 0],
+          rotation: null,
+          scale: null,
+          radiusTop: 1.2,
+          radiusBottom: 1.3,
+          height: 0.1,
+          radialSegments: 48,
+          castShadow: null,
+          receiveShadow: null,
+          material: {
+            color: "#1a1a1a",
+            metalness: 0.9,
+            roughness: 0.1,
+          },
+        },
+        children: [],
+      },
+      "ped-column": {
+        type: "Cylinder",
+        props: {
+          position: [0, 0.45, 0],
+          rotation: null,
+          scale: null,
+          radiusTop: 0.6,
+          radiusBottom: 0.8,
+          height: 0.7,
+          radialSegments: 48,
+          castShadow: null,
+          receiveShadow: null,
+          material: {
+            color: "#111111",
+            metalness: 0.95,
+            roughness: 0.05,
+          },
+        },
+        children: [],
+      },
+      "ped-top": {
+        type: "Cylinder",
+        props: {
+          position: [0, 0.85, 0],
+          rotation: null,
+          scale: null,
+          radiusTop: 0.9,
+          radiusBottom: 0.7,
+          height: 0.1,
+          radialSegments: 48,
+          castShadow: null,
+          receiveShadow: null,
+          material: {
+            color: "#1a1a1a",
+            metalness: 0.9,
+            roughness: 0.1,
+          },
+        },
+        children: [],
+      },
+      "hero-float": {
+        type: "Float",
+        props: {
+          position: [0, 2.2, 0],
+          rotation: null,
+          scale: null,
+          speed: 1.5,
+          rotationIntensity: 0.3,
+          floatIntensity: 0.4,
+          enabled: true,
+        },
+        children: ["hero-spin"],
+      },
+      "hero-spin": {
+        type: "Spin",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          speed: 0.3,
+          axis: "y",
+        },
+        children: ["hero-object"],
+      },
+      "hero-object": {
+        type: "TorusKnot",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.6,
+          tube: 0.22,
+          p: 2,
+          q: 3,
+          tubularSegments: 128,
+          radialSegments: 24,
+          material: {
+            color: "#ffffff",
+            metalness: 1,
+            roughness: 0,
+          },
+        },
+        children: [],
+      },
+      accent1: {
+        type: "Float",
+        props: {
+          position: [-1.5, 1.8, 0.5],
+          rotation: null,
+          scale: null,
+          speed: 2,
+          rotationIntensity: 0.5,
+          floatIntensity: 1.2,
+          enabled: true,
+        },
+        children: ["accent1-body"],
+      },
+      "accent1-body": {
+        type: "GlassSphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.15,
+          widthSegments: null,
+          heightSegments: null,
+          color: "#eeeeff",
+          transmission: 1,
+          thickness: 0.3,
+          roughness: 0,
+          chromaticAberration: 0.06,
+          ior: 1.5,
+          distortion: null,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      accent2: {
+        type: "Float",
+        props: {
+          position: [1.2, 2.5, -0.8],
+          rotation: null,
+          scale: null,
+          speed: 1.8,
+          rotationIntensity: 0.4,
+          floatIntensity: 1.5,
+          enabled: true,
+        },
+        children: ["accent2-body"],
+      },
+      "accent2-body": {
+        type: "GlassSphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.1,
+          widthSegments: null,
+          heightSegments: null,
+          color: "#ffeeff",
+          transmission: 1,
+          thickness: 0.2,
+          roughness: 0,
+          chromaticAberration: 0.08,
+          ior: 1.6,
+          distortion: null,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      accent3: {
+        type: "Float",
+        props: {
+          position: [0.8, 1.5, 1.2],
+          rotation: null,
+          scale: null,
+          speed: 2.2,
+          rotationIntensity: 0.6,
+          floatIntensity: 0.8,
+          enabled: true,
+        },
+        children: ["accent3-body"],
+      },
+      "accent3-body": {
+        type: "GlassSphere",
+        props: {
+          position: null,
+          rotation: null,
+          scale: null,
+          castShadow: null,
+          receiveShadow: null,
+          radius: 0.12,
+          widthSegments: null,
+          heightSegments: null,
+          color: "#eeffee",
+          transmission: 1,
+          thickness: 0.25,
+          roughness: 0,
+          chromaticAberration: 0.05,
+          ior: 1.4,
+          distortion: null,
+          distortionScale: null,
+          temporalDistortion: null,
+          samples: 6,
+          resolution: 128,
+        },
+        children: [],
+      },
+      floor: {
+        type: "ReflectorPlane",
+        props: {
+          position: [0, -0.5, 0],
+          rotation: [-1.5708, 0, 0],
+          scale: null,
+          width: 25,
+          height: 25,
+          color: "#0a0a0a",
+          resolution: 1024,
+          blur: 300,
+          mirror: 0.6,
+          mixBlur: 8,
+          mixStrength: 2,
+          depthScale: 0.2,
+          metalness: 0.8,
+          roughness: 0.3,
+        },
+        children: [],
+      },
+      shadows: {
+        type: "ContactShadows",
+        props: {
+          position: [0, -0.49, 0],
+          rotation: null,
+          scale: null,
+          opacity: 0.5,
+          width: 10,
+          height: 10,
+          blur: 2,
+          near: null,
+          far: 5,
+          smooth: true,
+          resolution: 512,
+          frames: null,
+          color: "#000000",
+        },
+        children: [],
+      },
+      sparkles: {
+        type: "Sparkles",
+        props: {
+          position: [0, 2, 0],
+          rotation: null,
+          scale: [6, 4, 6],
+          count: 30,
+          speed: 0.05,
+          opacity: 0.15,
+          color: "#ffffff",
+          size: 0.3,
+          noise: 0.5,
+        },
+        children: [],
+      },
+      fx: {
+        type: "EffectComposer",
+        props: { enabled: true, multisampling: 8 },
+        children: ["bloom", "vignette"],
+      },
+      bloom: {
+        type: "Bloom",
+        props: {
+          intensity: 0.5,
+          luminanceThreshold: 0.3,
+          luminanceSmoothing: 0.4,
+          mipmapBlur: true,
+        },
+        children: [],
+      },
+      vignette: {
+        type: "Vignette",
+        props: { offset: 0.3, darkness: 0.5 },
+        children: [],
+      },
+      controls: {
+        type: "OrbitControls",
+        props: {
+          enableDamping: true,
+          dampingFactor: null,
+          enableZoom: null,
+          enablePan: null,
+          enableRotate: null,
+          minDistance: 3,
+          maxDistance: 12,
+          minPolarAngle: 0.3,
+          maxPolarAngle: 1.4,
+          autoRotate: true,
+          autoRotateSpeed: 1,
+          target: [0, 1.5, 0],
+        },
+        children: [],
+      },
+    },
+  },
+};

+ 149 - 0
examples/react-three-fiber/app/scenes/starfield.ts

@@ -0,0 +1,149 @@
+import type { Scene } from "./_helpers";
+
+export const starfield: Scene = {
+  name: "Starfield",
+  description:
+    "Classic Windows starfield screensaver -- flying through stars at warp speed",
+  spec: {
+    root: "scene",
+    elements: {
+      scene: {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "cam",
+          "ambient",
+          "tunnel",
+          "stars1",
+          "stars2",
+          "sparkle-dust",
+          "shake",
+          "post",
+          "controls",
+        ],
+      },
+      cam: {
+        type: "PerspectiveCamera",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          fov: 75,
+          near: 0.1,
+          far: 1000,
+          makeDefault: true,
+        },
+        children: [],
+      },
+      ambient: {
+        type: "AmbientLight",
+        props: { color: "#111122", intensity: 0.15 },
+        children: [],
+      },
+      tunnel: {
+        type: "WarpTunnel",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          ringCount: 120,
+          radius: 4,
+          length: 40,
+          speed: 12,
+          tubeRadius: 0.008,
+          color1: "#ffffff",
+          color2: "#aabbff",
+        },
+        children: [],
+      },
+      stars1: {
+        type: "Stars",
+        props: {
+          radius: 50,
+          depth: 80,
+          count: 10000,
+          factor: 6,
+          saturation: 0,
+          fade: true,
+          speed: 3,
+        },
+        children: [],
+      },
+      stars2: {
+        type: "Stars",
+        props: {
+          radius: 30,
+          depth: 50,
+          count: 5000,
+          factor: 3,
+          saturation: 0.1,
+          fade: true,
+          speed: 5,
+        },
+        children: [],
+      },
+      "sparkle-dust": {
+        type: "Sparkles",
+        props: {
+          position: [0, 0, -5],
+          rotation: null,
+          scale: [8, 8, 30],
+          count: 300,
+          speed: 2,
+          opacity: 0.6,
+          color: "#ffffff",
+          size: 1,
+          noise: 4,
+        },
+        children: [],
+      },
+      shake: {
+        type: "CameraShake",
+        props: {
+          intensity: 0.15,
+          maxYaw: 0.02,
+          maxPitch: 0.02,
+          maxRoll: 0.01,
+        },
+        children: [],
+      },
+      post: {
+        type: "EffectComposer",
+        props: { enabled: true, multisampling: 8 },
+        children: ["bloom", "vignette"],
+      },
+      bloom: {
+        type: "Bloom",
+        props: {
+          intensity: 1.5,
+          luminanceThreshold: 0.15,
+          luminanceSmoothing: null,
+          mipmapBlur: true,
+        },
+        children: [],
+      },
+      vignette: {
+        type: "Vignette",
+        props: { offset: 0.1, darkness: 0.9 },
+        children: [],
+      },
+      controls: {
+        type: "OrbitControls",
+        props: {
+          enableDamping: null,
+          dampingFactor: null,
+          enableZoom: false,
+          enablePan: false,
+          enableRotate: false,
+          minDistance: null,
+          maxDistance: null,
+          minPolarAngle: null,
+          maxPolarAngle: null,
+          autoRotate: false,
+          autoRotateSpeed: null,
+        },
+        children: [],
+      },
+    },
+  },
+};

+ 295 - 0
examples/react-three-fiber/app/scenes/storm-cell.ts

@@ -0,0 +1,295 @@
+import type { Scene } from "./_helpers";
+
+export const stormCell: Scene = {
+  name: "Storm Cell",
+  description:
+    "Dark violent thunderstorm -- spinning cloud masses, pulsing lightning, rain particles, camera shake",
+  spec: {
+    root: "scene",
+    elements: {
+      scene: {
+        type: "Group",
+        props: { position: null, rotation: null, scale: null },
+        children: [
+          "sky",
+          "ambient",
+          "sun",
+          "cloud-spin-1",
+          "cloud-spin-2",
+          "cloud-spin-3",
+          "lightning-1-pulse",
+          "lightning-2-pulse",
+          "lightning-3-pulse",
+          "rain",
+          "shake",
+          "post",
+          "fog",
+          "controls",
+        ],
+      },
+      sky: {
+        type: "Sky",
+        props: {
+          distance: null,
+          sunPosition: [10, 2, 0],
+          inclination: null,
+          azimuth: null,
+          turbidity: 20,
+          rayleigh: 0.5,
+          mieCoefficient: 0.01,
+          mieDirectionalG: 0.9,
+        },
+        children: [],
+      },
+      ambient: {
+        type: "AmbientLight",
+        props: { color: "#111122", intensity: 0.15 },
+        children: [],
+      },
+      sun: {
+        type: "DirectionalLight",
+        props: {
+          position: [10, 3, 0],
+          rotation: null,
+          scale: null,
+          color: "#334466",
+          intensity: 0.5,
+          castShadow: true,
+        },
+        children: [],
+      },
+      "cloud-spin-1": {
+        type: "Spin",
+        props: {
+          position: [0, 4, 0],
+          rotation: null,
+          scale: null,
+          speed: 0.08,
+          axis: "y",
+        },
+        children: ["cloud-1"],
+      },
+      "cloud-1": {
+        type: "Cloud",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          seed: 1,
+          segments: 40,
+          bounds: [25, 3, 25],
+          volume: 15,
+          speed: 0.3,
+          fade: 20,
+          opacity: 0.9,
+          color: "#222233",
+          growth: 10,
+        },
+        children: [],
+      },
+      "cloud-spin-2": {
+        type: "Spin",
+        props: {
+          position: [0, 6, -3],
+          rotation: null,
+          scale: null,
+          speed: -0.05,
+          axis: "y",
+        },
+        children: ["cloud-2"],
+      },
+      "cloud-2": {
+        type: "Cloud",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          seed: 42,
+          segments: 30,
+          bounds: [20, 2, 20],
+          volume: 12,
+          speed: 0.2,
+          fade: 15,
+          opacity: 0.7,
+          color: "#1a1a2a",
+          growth: 8,
+        },
+        children: [],
+      },
+      "cloud-spin-3": {
+        type: "Spin",
+        props: {
+          position: [0, 8, 2],
+          rotation: null,
+          scale: null,
+          speed: 0.03,
+          axis: "y",
+        },
+        children: ["cloud-3"],
+      },
+      "cloud-3": {
+        type: "Cloud",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          seed: 99,
+          segments: 25,
+          bounds: [30, 2, 30],
+          volume: 10,
+          speed: 0.15,
+          fade: 25,
+          opacity: 0.5,
+          color: "#2a2a3a",
+          growth: 6,
+        },
+        children: [],
+      },
+      "lightning-1-pulse": {
+        type: "Pulse",
+        props: {
+          position: [2, 5, -1],
+          rotation: null,
+          scale: null,
+          speed: 3,
+          min: 0,
+          max: 1,
+        },
+        children: ["lightning-1"],
+      },
+      "lightning-1": {
+        type: "PointLight",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          color: "#aabbff",
+          intensity: 80,
+          distance: 30,
+          decay: 2,
+          castShadow: false,
+        },
+        children: [],
+      },
+      "lightning-2-pulse": {
+        type: "Pulse",
+        props: {
+          position: [-3, 6, 3],
+          rotation: null,
+          scale: null,
+          speed: 4.5,
+          min: 0,
+          max: 1,
+        },
+        children: ["lightning-2"],
+      },
+      "lightning-2": {
+        type: "PointLight",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          color: "#aaccff",
+          intensity: 60,
+          distance: 25,
+          decay: 2,
+          castShadow: false,
+        },
+        children: [],
+      },
+      "lightning-3-pulse": {
+        type: "Pulse",
+        props: {
+          position: [0, 4, -4],
+          rotation: null,
+          scale: null,
+          speed: 2.5,
+          min: 0,
+          max: 1,
+        },
+        children: ["lightning-3"],
+      },
+      "lightning-3": {
+        type: "PointLight",
+        props: {
+          position: [0, 0, 0],
+          rotation: null,
+          scale: null,
+          color: "#99bbff",
+          intensity: 40,
+          distance: 20,
+          decay: 2,
+          castShadow: false,
+        },
+        children: [],
+      },
+      rain: {
+        type: "Sparkles",
+        props: {
+          position: [0, 5, 0],
+          rotation: null,
+          scale: [20, 15, 20],
+          count: 500,
+          speed: 5,
+          opacity: 0.4,
+          color: "#6688aa",
+          size: 0.5,
+          noise: 0.5,
+        },
+        children: [],
+      },
+      shake: {
+        type: "CameraShake",
+        props: {
+          intensity: 0.2,
+          maxYaw: 0.02,
+          maxPitch: 0.03,
+          maxRoll: 0.01,
+        },
+        children: [],
+      },
+      post: {
+        type: "EffectComposer",
+        props: { enabled: true, multisampling: 8 },
+        children: ["bloom", "vignette"],
+      },
+      bloom: {
+        type: "Bloom",
+        props: {
+          intensity: 0.8,
+          luminanceThreshold: 0.3,
+          luminanceSmoothing: null,
+          mipmapBlur: true,
+        },
+        children: [],
+      },
+      vignette: {
+        type: "Vignette",
+        props: { offset: 0.3, darkness: 0.8 },
+        children: [],
+      },
+      fog: {
+        type: "Fog",
+        props: { color: "#0a0a15", near: 5, far: 30 },
+        children: [],
+      },
+      controls: {
+        type: "OrbitControls",
+        props: {
+          enableDamping: null,
+          dampingFactor: null,
+          enableZoom: true,
+          enablePan: null,
+          enableRotate: true,
+          minDistance: 3,
+          maxDistance: 20,
+          minPolarAngle: 0.2,
+          maxPolarAngle: 1.3,
+          autoRotate: true,
+          autoRotateSpeed: 0.1,
+        },
+        children: [],
+      },
+    },
+  },
+};

+ 12 - 0
examples/react-three-fiber/eslint.config.js

@@ -0,0 +1,12 @@
+import { nextJsConfig } from "@internal/eslint-config/next-js";
+
+/** @type {import("eslint").Linter.Config[]} */
+export default [
+  ...nextJsConfig,
+  {
+    rules: {
+      "react/prop-types": "off",
+      "react/no-unknown-property": "off",
+    },
+  },
+];

+ 6 - 0
examples/react-three-fiber/next-env.d.ts

@@ -0,0 +1,6 @@
+/// <reference types="next" />
+/// <reference types="next/image-types/global" />
+import "./.next/dev/types/routes.d.ts";
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

+ 12 - 0
examples/react-three-fiber/next.config.ts

@@ -0,0 +1,12 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+  transpilePackages: [
+    "@json-render/core",
+    "@json-render/react",
+    "@json-render/react-three-fiber",
+    "three",
+  ],
+};
+
+export default nextConfig;

+ 35 - 0
examples/react-three-fiber/package.json

@@ -0,0 +1,35 @@
+{
+  "name": "example-react-three-fiber",
+  "version": "0.1.6",
+  "type": "module",
+  "private": true,
+  "scripts": {
+    "predev": "command -v portless >/dev/null 2>&1 || (echo '\\nportless is required but not installed. Run: npm i -g portless\\nSee: https://github.com/vercel-labs/portless\\n' && exit 1)",
+    "dev": "portless react-three-fiber-demo.json-render next dev --turbopack",
+    "build": "next build",
+    "start": "next start",
+    "lint": "eslint --max-warnings 0",
+    "check-types": "tsc --noEmit"
+  },
+  "dependencies": {
+    "@json-render/core": "workspace:*",
+    "@json-render/react": "workspace:*",
+    "@json-render/react-three-fiber": "workspace:*",
+    "@react-three/drei": "^10.7.7",
+    "@react-three/fiber": "^9.5.0",
+    "next": "16.1.6",
+    "react": "19.2.4",
+    "react-dom": "19.2.4",
+    "three": "^0.183.2",
+    "zod": "4.3.6"
+  },
+  "devDependencies": {
+    "@internal/eslint-config": "workspace:*",
+    "@types/node": "^22.10.0",
+    "@types/react": "19.2.3",
+    "@types/react-dom": "19.2.3",
+    "@types/three": "^0.183.1",
+    "eslint": "^9.39.1",
+    "typescript": "^5.7.2"
+  }
+}

+ 13 - 0
examples/react-three-fiber/tsconfig.json

@@ -0,0 +1,13 @@
+{
+  "extends": "../../packages/typescript-config/nextjs.json",
+  "compilerOptions": {
+    "plugins": [{ "name": "next" }],
+    "declaration": false,
+    "declarationMap": false,
+    "paths": {
+      "@/*": ["./*"]
+    }
+  },
+  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+  "exclude": ["node_modules"]
+}

+ 75 - 0
packages/react-three-fiber/README.md

@@ -0,0 +1,75 @@
+# @json-render/react-three-fiber
+
+React Three Fiber renderer for [`@json-render/core`](https://json-render.dev). JSON becomes 3D scenes.
+
+19 built-in components for meshes, lights, models, environments, text, cameras, and controls.
+
+## Installation
+
+```bash
+npm install @json-render/react-three-fiber @json-render/core @json-render/react @react-three/fiber @react-three/drei three zod
+```
+
+## Usage
+
+```tsx
+import { defineCatalog } from "@json-render/core";
+import { schema, defineRegistry } from "@json-render/react";
+import {
+  threeComponentDefinitions,
+  threeComponents,
+  ThreeCanvas,
+} from "@json-render/react-three-fiber";
+
+// Build catalog with 3D components
+const catalog = defineCatalog(schema, {
+  components: {
+    Box: threeComponentDefinitions.Box,
+    Sphere: threeComponentDefinitions.Sphere,
+    AmbientLight: threeComponentDefinitions.AmbientLight,
+    DirectionalLight: threeComponentDefinitions.DirectionalLight,
+    OrbitControls: threeComponentDefinitions.OrbitControls,
+  },
+  actions: {},
+});
+
+// Register implementations
+const { registry } = defineRegistry(catalog, {
+  components: {
+    Box: threeComponents.Box,
+    Sphere: threeComponents.Sphere,
+    AmbientLight: threeComponents.AmbientLight,
+    DirectionalLight: threeComponents.DirectionalLight,
+    OrbitControls: threeComponents.OrbitControls,
+  },
+});
+
+// Render
+<ThreeCanvas
+  spec={spec}
+  registry={registry}
+  shadows
+  camera={{ position: [5, 5, 5], fov: 50 }}
+  style={{ width: "100%", height: "100vh" }}
+/>;
+```
+
+## Components
+
+**Primitives:** Box, Sphere, Cylinder, Cone, Torus, Plane, Capsule
+
+**Lights:** AmbientLight, DirectionalLight, PointLight, SpotLight
+
+**Container:** Group
+
+**Model:** Model (GLTF/GLB)
+
+**Environment:** Environment, Fog, GridHelper
+
+**Text:** Text3D
+
+**Camera/Controls:** PerspectiveCamera, OrbitControls
+
+## Docs
+
+Full API reference: [json-render.dev/docs/api/three](https://json-render.dev/docs/api/three)

+ 80 - 0
packages/react-three-fiber/package.json

@@ -0,0 +1,80 @@
+{
+  "name": "@json-render/react-three-fiber",
+  "version": "0.12.1",
+  "license": "Apache-2.0",
+  "description": "React Three Fiber renderer for @json-render/core. JSON becomes 3D scenes.",
+  "keywords": [
+    "json",
+    "3d",
+    "three",
+    "threejs",
+    "react-three-fiber",
+    "r3f",
+    "webgl",
+    "ai",
+    "generative-ui",
+    "llm",
+    "renderer",
+    "streaming",
+    "components"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/vercel-labs/json-render.git",
+    "directory": "packages/react-three-fiber"
+  },
+  "homepage": "https://json-render.dev",
+  "bugs": {
+    "url": "https://github.com/vercel-labs/json-render/issues"
+  },
+  "publishConfig": {
+    "access": "public"
+  },
+  "main": "./dist/index.js",
+  "module": "./dist/index.mjs",
+  "types": "./dist/index.d.ts",
+  "exports": {
+    ".": {
+      "types": "./dist/index.d.ts",
+      "import": "./dist/index.mjs",
+      "require": "./dist/index.js"
+    },
+    "./catalog": {
+      "types": "./dist/catalog.d.ts",
+      "import": "./dist/catalog.mjs",
+      "require": "./dist/catalog.js"
+    }
+  },
+  "files": [
+    "dist"
+  ],
+  "scripts": {
+    "build": "tsup",
+    "dev": "tsup --watch",
+    "check-types": "tsc --noEmit",
+    "typecheck": "tsc --noEmit"
+  },
+  "dependencies": {
+    "@json-render/core": "workspace:*",
+    "@json-render/react": "workspace:*",
+    "@react-three/postprocessing": "^3.0.4"
+  },
+  "devDependencies": {
+    "@internal/typescript-config": "workspace:*",
+    "@react-three/drei": "^10.7.7",
+    "@react-three/fiber": "^9.5.0",
+    "@types/react": "19.2.3",
+    "@types/three": "^0.183.1",
+    "three": "^0.183.2",
+    "tsup": "^8.0.2",
+    "typescript": "^5.4.5",
+    "zod": "^4.3.6"
+  },
+  "peerDependencies": {
+    "@react-three/drei": ">=9.0.0",
+    "@react-three/fiber": ">=8.0.0",
+    "react": "^19.0.0",
+    "three": ">=0.160.0",
+    "zod": "^4.0.0"
+  }
+}

+ 841 - 0
packages/react-three-fiber/src/catalog.ts

@@ -0,0 +1,841 @@
+import { z } from "zod";
+import {
+  vector3Schema,
+  materialSchema,
+  transformProps,
+  shadowProps,
+} from "./schemas";
+
+/**
+ * React Three Fiber component definitions for json-render catalogs.
+ *
+ * These can be used directly or extended with custom 3D components.
+ * All components render as React Three Fiber elements.
+ */
+export const threeComponentDefinitions = {
+  // ===========================================================================
+  // Primitives
+  // ===========================================================================
+
+  Box: {
+    props: z.object({
+      ...transformProps,
+      ...shadowProps,
+      material: materialSchema.nullable(),
+      width: z.number().nullable(),
+      height: z.number().nullable(),
+      depth: z.number().nullable(),
+    }),
+    description:
+      "Box mesh. Defaults to 1x1x1. Use material for appearance, transform props for placement.",
+    example: {
+      position: [0, 0.5, 0],
+      material: { color: "#4488ff" },
+    },
+  },
+
+  Sphere: {
+    props: z.object({
+      ...transformProps,
+      ...shadowProps,
+      material: materialSchema.nullable(),
+      radius: z.number().nullable(),
+      widthSegments: z.number().nullable(),
+      heightSegments: z.number().nullable(),
+    }),
+    description: "Sphere mesh. Defaults to radius 1.",
+    example: {
+      position: [0, 1, 0],
+      radius: 0.5,
+      material: { color: "#ff4444" },
+    },
+  },
+
+  Cylinder: {
+    props: z.object({
+      ...transformProps,
+      ...shadowProps,
+      material: materialSchema.nullable(),
+      radiusTop: z.number().nullable(),
+      radiusBottom: z.number().nullable(),
+      height: z.number().nullable(),
+      radialSegments: z.number().nullable(),
+    }),
+    description: "Cylinder mesh. Use radiusTop/radiusBottom for tapering.",
+    example: {
+      position: [0, 0.5, 0],
+      height: 1,
+      material: { color: "#44ff44" },
+    },
+  },
+
+  Cone: {
+    props: z.object({
+      ...transformProps,
+      ...shadowProps,
+      material: materialSchema.nullable(),
+      radius: z.number().nullable(),
+      height: z.number().nullable(),
+      radialSegments: z.number().nullable(),
+    }),
+    description: "Cone mesh.",
+    example: {
+      position: [0, 0.5, 0],
+      material: { color: "#ffaa00" },
+    },
+  },
+
+  Torus: {
+    props: z.object({
+      ...transformProps,
+      ...shadowProps,
+      material: materialSchema.nullable(),
+      radius: z.number().nullable(),
+      tube: z.number().nullable(),
+      radialSegments: z.number().nullable(),
+      tubularSegments: z.number().nullable(),
+    }),
+    description: "Torus (donut) mesh.",
+    example: {
+      position: [0, 1, 0],
+      material: { color: "#ff44ff" },
+    },
+  },
+
+  Plane: {
+    props: z.object({
+      ...transformProps,
+      ...shadowProps,
+      material: materialSchema.nullable(),
+      width: z.number().nullable(),
+      height: z.number().nullable(),
+    }),
+    slots: ["default"],
+    description:
+      "Flat plane mesh. Useful for floors/walls. Defaults to XY plane; rotate [-Math.PI/2, 0, 0] for ground. Pass MeshPortalMaterial as child for portal surfaces.",
+    example: {
+      position: [0, 0, 0],
+      rotation: [-1.5708, 0, 0],
+      scale: [10, 10, 1],
+      material: { color: "#888888" },
+      receiveShadow: true,
+    },
+  },
+
+  Capsule: {
+    props: z.object({
+      ...transformProps,
+      ...shadowProps,
+      material: materialSchema.nullable(),
+      radius: z.number().nullable(),
+      length: z.number().nullable(),
+      capSegments: z.number().nullable(),
+      radialSegments: z.number().nullable(),
+    }),
+    description: "Capsule mesh (cylinder with hemispherical caps).",
+    example: {
+      position: [0, 1, 0],
+      material: { color: "#00cccc" },
+    },
+  },
+
+  // ===========================================================================
+  // Lights
+  // ===========================================================================
+
+  AmbientLight: {
+    props: z.object({
+      color: z.string().nullable(),
+      intensity: z.number().nullable(),
+    }),
+    description:
+      "Ambient light that illuminates all objects equally. No position needed.",
+    example: { color: "#ffffff", intensity: 0.5 },
+  },
+
+  DirectionalLight: {
+    props: z.object({
+      ...transformProps,
+      color: z.string().nullable(),
+      intensity: z.number().nullable(),
+      castShadow: z.boolean().nullable(),
+    }),
+    description:
+      "Directional light (like sunlight). Position determines the direction it shines from.",
+    example: {
+      position: [5, 10, 5],
+      intensity: 1,
+      castShadow: true,
+    },
+  },
+
+  PointLight: {
+    props: z.object({
+      ...transformProps,
+      color: z.string().nullable(),
+      intensity: z.number().nullable(),
+      distance: z.number().nullable(),
+      decay: z.number().nullable(),
+      castShadow: z.boolean().nullable(),
+    }),
+    description: "Point light that radiates in all directions from a position.",
+    example: {
+      position: [0, 3, 0],
+      intensity: 1,
+      distance: 10,
+    },
+  },
+
+  SpotLight: {
+    props: z.object({
+      ...transformProps,
+      color: z.string().nullable(),
+      intensity: z.number().nullable(),
+      distance: z.number().nullable(),
+      decay: z.number().nullable(),
+      angle: z.number().nullable(),
+      penumbra: z.number().nullable(),
+      castShadow: z.boolean().nullable(),
+    }),
+    description:
+      "Spot light emitting a cone of light. Angle in radians (default ~Math.PI/3).",
+    example: {
+      position: [0, 5, 0],
+      intensity: 1,
+      angle: 0.5,
+      penumbra: 0.5,
+      castShadow: true,
+    },
+  },
+
+  // ===========================================================================
+  // Container
+  // ===========================================================================
+
+  Group: {
+    props: z.object({
+      ...transformProps,
+    }),
+    slots: ["default"],
+    description:
+      "Container for grouping child objects. Transforms apply to all children.",
+    example: { position: [0, 0, 0] },
+  },
+
+  // ===========================================================================
+  // Model
+  // ===========================================================================
+
+  Model: {
+    props: z.object({
+      ...transformProps,
+      ...shadowProps,
+      url: z.string(),
+    }),
+    description:
+      "GLTF/GLB 3D model loader. Provide a URL to a .glb or .gltf file.",
+    example: {
+      url: "/models/robot.glb",
+      position: [0, 0, 0],
+      scale: [1, 1, 1],
+    },
+  },
+
+  // ===========================================================================
+  // Environment
+  // ===========================================================================
+
+  Environment: {
+    props: z.object({
+      preset: z
+        .enum([
+          "apartment",
+          "city",
+          "dawn",
+          "forest",
+          "lobby",
+          "night",
+          "park",
+          "studio",
+          "sunset",
+          "warehouse",
+        ])
+        .nullable(),
+      background: z.boolean().nullable(),
+      blur: z.number().nullable(),
+      intensity: z.number().nullable(),
+    }),
+    description:
+      "HDRI environment map for lighting and background. Choose a preset or provide a custom HDRI.",
+    example: { preset: "sunset", background: true },
+  },
+
+  Fog: {
+    props: z.object({
+      color: z.string().nullable(),
+      near: z.number().nullable(),
+      far: z.number().nullable(),
+    }),
+    description:
+      "Linear fog effect. Objects fade between near and far distances.",
+    example: { color: "#cccccc", near: 10, far: 50 },
+  },
+
+  GridHelper: {
+    props: z.object({
+      ...transformProps,
+      size: z.number().nullable(),
+      divisions: z.number().nullable(),
+      color: z.string().nullable(),
+      secondaryColor: z.string().nullable(),
+    }),
+    description: "Visual grid for reference. Renders on the XZ plane.",
+    example: { size: 10, divisions: 10 },
+  },
+
+  // ===========================================================================
+  // Text
+  // ===========================================================================
+
+  Text3D: {
+    props: z.object({
+      ...transformProps,
+      text: z.string(),
+      fontSize: z.number().nullable(),
+      color: z.string().nullable(),
+      anchorX: z.enum(["left", "center", "right"]).nullable(),
+      anchorY: z
+        .enum(["top", "top-baseline", "middle", "bottom-baseline", "bottom"])
+        .nullable(),
+      maxWidth: z.number().nullable(),
+    }),
+    description:
+      "3D text rendered in the scene. Uses SDF text for crisp rendering at any size.",
+    example: {
+      text: "Hello World",
+      fontSize: 1,
+      color: "#ffffff",
+      position: [0, 2, 0],
+    },
+  },
+
+  // ===========================================================================
+  // Effects / Atmosphere
+  // ===========================================================================
+
+  Sparkles: {
+    props: z.object({
+      ...transformProps,
+      count: z.number().nullable(),
+      speed: z.number().nullable(),
+      opacity: z.number().nullable(),
+      color: z.string().nullable(),
+      size: z.number().nullable(),
+      noise: z.number().nullable(),
+    }),
+    description:
+      "Floating particle sparkles. Great for magic, snow, or ambient effects.",
+    example: {
+      count: 100,
+      speed: 0.5,
+      size: 2,
+      color: "#ffffff",
+      scale: [5, 5, 5],
+    },
+  },
+
+  Stars: {
+    props: z.object({
+      radius: z.number().nullable(),
+      depth: z.number().nullable(),
+      count: z.number().nullable(),
+      factor: z.number().nullable(),
+      saturation: z.number().nullable(),
+      fade: z.boolean().nullable(),
+      speed: z.number().nullable(),
+    }),
+    description:
+      "Starfield background. Renders thousands of stars in a sphere around the scene.",
+    example: { radius: 100, depth: 50, count: 5000, factor: 4, fade: true },
+  },
+
+  Sky: {
+    props: z.object({
+      distance: z.number().nullable(),
+      sunPosition: vector3Schema.nullable(),
+      inclination: z.number().nullable(),
+      azimuth: z.number().nullable(),
+      mieCoefficient: z.number().nullable(),
+      mieDirectionalG: z.number().nullable(),
+      rayleigh: z.number().nullable(),
+      turbidity: z.number().nullable(),
+    }),
+    description:
+      "Procedural sky with sun. Control sun position, haze, and scattering.",
+    example: { sunPosition: [100, 20, 100], turbidity: 8, rayleigh: 2 },
+  },
+
+  Cloud: {
+    props: z.object({
+      ...transformProps,
+      seed: z.number().nullable(),
+      segments: z.number().nullable(),
+      bounds: vector3Schema.nullable(),
+      volume: z.number().nullable(),
+      speed: z.number().nullable(),
+      fade: z.number().nullable(),
+      opacity: z.number().nullable(),
+      color: z.string().nullable(),
+      growth: z.number().nullable(),
+    }),
+    description:
+      "Volumetric cloud. Use multiple for a cloudscape. Wrap in a Clouds parent for batching.",
+    example: {
+      position: [0, 5, 0],
+      speed: 0.2,
+      opacity: 0.6,
+      color: "#ffffff",
+    },
+  },
+
+  // ===========================================================================
+  // Special Materials (geometry + material combos)
+  // ===========================================================================
+
+  GlassSphere: {
+    props: z.object({
+      ...transformProps,
+      ...shadowProps,
+      radius: z.number().nullable(),
+      widthSegments: z.number().nullable(),
+      heightSegments: z.number().nullable(),
+      color: z.string().nullable(),
+      transmission: z.number().nullable(),
+      thickness: z.number().nullable(),
+      roughness: z.number().nullable(),
+      chromaticAberration: z.number().nullable(),
+      ior: z.number().nullable(),
+      distortion: z.number().nullable(),
+      distortionScale: z.number().nullable(),
+      temporalDistortion: z.number().nullable(),
+      samples: z.number().nullable(),
+      resolution: z.number().nullable(),
+    }),
+    description:
+      "Glass sphere with transmission/refraction. Creates photorealistic glass effect.",
+    example: {
+      position: [0, 1, 0],
+      radius: 1,
+      transmission: 1,
+      thickness: 0.5,
+      roughness: 0,
+      chromaticAberration: 0.06,
+    },
+  },
+
+  GlassBox: {
+    props: z.object({
+      ...transformProps,
+      ...shadowProps,
+      width: z.number().nullable(),
+      height: z.number().nullable(),
+      depth: z.number().nullable(),
+      color: z.string().nullable(),
+      transmission: z.number().nullable(),
+      thickness: z.number().nullable(),
+      roughness: z.number().nullable(),
+      chromaticAberration: z.number().nullable(),
+      ior: z.number().nullable(),
+      distortion: z.number().nullable(),
+      distortionScale: z.number().nullable(),
+      temporalDistortion: z.number().nullable(),
+      samples: z.number().nullable(),
+      resolution: z.number().nullable(),
+    }),
+    description:
+      "Glass box with transmission/refraction. Photorealistic glass cuboid.",
+    example: {
+      position: [0, 0.5, 0],
+      width: 1,
+      height: 1,
+      depth: 1,
+      transmission: 1,
+      thickness: 0.5,
+    },
+  },
+
+  DistortSphere: {
+    props: z.object({
+      ...transformProps,
+      ...shadowProps,
+      radius: z.number().nullable(),
+      widthSegments: z.number().nullable(),
+      heightSegments: z.number().nullable(),
+      color: z.string().nullable(),
+      speed: z.number().nullable(),
+      distort: z.number().nullable(),
+      metalness: z.number().nullable(),
+      roughness: z.number().nullable(),
+    }),
+    description:
+      "Animated distorting sphere. Organic, blobby look -- like liquid metal.",
+    example: {
+      position: [0, 1, 0],
+      radius: 1,
+      color: "#ff6600",
+      speed: 2,
+      distort: 0.5,
+    },
+  },
+
+  // ===========================================================================
+  // Extended Geometry
+  // ===========================================================================
+
+  TorusKnot: {
+    props: z.object({
+      ...transformProps,
+      ...shadowProps,
+      material: materialSchema.nullable(),
+      radius: z.number().nullable(),
+      tube: z.number().nullable(),
+      tubularSegments: z.number().nullable(),
+      radialSegments: z.number().nullable(),
+      p: z.number().nullable(),
+      q: z.number().nullable(),
+    }),
+    description:
+      "Torus knot mesh. A continuous 3D curve that creates intricate knot shapes via p and q parameters.",
+    example: {
+      position: [0, 1, 0],
+      radius: 1,
+      tube: 0.3,
+      p: 2,
+      q: 3,
+      material: { color: "#ff44ff", metalness: 0.8, roughness: 0.2 },
+    },
+  },
+
+  RoundedBox: {
+    props: z.object({
+      ...transformProps,
+      ...shadowProps,
+      material: materialSchema.nullable(),
+      width: z.number().nullable(),
+      height: z.number().nullable(),
+      depth: z.number().nullable(),
+      radius: z.number().nullable(),
+      smoothness: z.number().nullable(),
+    }),
+    description:
+      "Box with rounded edges. More polished look than a plain box. Great for product-style scenes.",
+    example: {
+      position: [0, 0.5, 0],
+      width: 1,
+      height: 1,
+      depth: 1,
+      radius: 0.1,
+      material: { color: "#4488ff" },
+    },
+  },
+
+  // ===========================================================================
+  // Shadows / Staging
+  // ===========================================================================
+
+  ContactShadows: {
+    props: z.object({
+      ...transformProps,
+      opacity: z.number().nullable(),
+      width: z.number().nullable(),
+      height: z.number().nullable(),
+      blur: z.number().nullable(),
+      near: z.number().nullable(),
+      far: z.number().nullable(),
+      smooth: z.boolean().nullable(),
+      resolution: z.number().nullable(),
+      frames: z.number().nullable(),
+      color: z.string().nullable(),
+    }),
+    description:
+      "Soft contact shadows projected onto the ground plane. Place at y=0 under objects.",
+    example: {
+      position: [0, 0, 0],
+      opacity: 0.5,
+      blur: 2,
+      width: 10,
+      height: 10,
+    },
+  },
+
+  Float: {
+    props: z.object({
+      ...transformProps,
+      speed: z.number().nullable(),
+      rotationIntensity: z.number().nullable(),
+      floatIntensity: z.number().nullable(),
+      enabled: z.boolean().nullable(),
+    }),
+    slots: ["default"],
+    description:
+      "Wrapper that makes children gently float and bob. Adds organic motion to any object.",
+    example: { speed: 1.5, rotationIntensity: 1, floatIntensity: 1 },
+  },
+
+  ReflectorPlane: {
+    props: z.object({
+      ...transformProps,
+      width: z.number().nullable(),
+      height: z.number().nullable(),
+      color: z.string().nullable(),
+      resolution: z.number().nullable(),
+      blur: z.number().nullable(),
+      mirror: z.number().nullable(),
+      mixBlur: z.number().nullable(),
+      mixStrength: z.number().nullable(),
+      depthScale: z.number().nullable(),
+      metalness: z.number().nullable(),
+      roughness: z.number().nullable(),
+    }),
+    description:
+      "Reflective floor/surface. Real-time mirror reflections on a plane. Rotate [-PI/2,0,0] for ground.",
+    example: {
+      position: [0, 0, 0],
+      rotation: [-1.5708, 0, 0],
+      width: 20,
+      height: 20,
+      mirror: 0.5,
+      blur: [300, 100],
+      resolution: 1024,
+    },
+  },
+
+  Backdrop: {
+    props: z.object({
+      ...transformProps,
+      floor: z.number().nullable(),
+      segments: z.number().nullable(),
+      receiveShadow: z.boolean().nullable(),
+    }),
+    slots: ["default"],
+    description:
+      "Curved studio backdrop for product-style scenes. Set floor for how much bends.",
+    example: { floor: 0.25, segments: 20, receiveShadow: true },
+  },
+
+  // ===========================================================================
+  // Crazy / Magic
+  // ===========================================================================
+
+  MeshPortalMaterial: {
+    props: z.object({
+      blend: z.number().nullable(),
+      blur: z.number().nullable(),
+      resolution: z.number().nullable(),
+    }),
+    slots: ["default"],
+    description:
+      "Renders children into a portal surface (like a window to another world). Use as a child of a Mesh.",
+    example: { blend: 0 },
+  },
+
+  HtmlLabel: {
+    props: z.object({
+      ...transformProps,
+      text: z.string(),
+      transform: z.boolean().nullable(),
+      distanceFactor: z.number().nullable(),
+      color: z.string().nullable(),
+      fontSize: z.number().nullable(),
+      center: z.boolean().nullable(),
+    }),
+    description: "Renders actual HTML/DOM text floating in the 3D scene.",
+    example: { text: "Hello World", transform: true, distanceFactor: 10 },
+  },
+
+  // ===========================================================================
+  // Post-Processing
+  // ===========================================================================
+
+  EffectComposer: {
+    props: z.object({
+      enabled: z.boolean().nullable(),
+      multisampling: z.number().nullable(),
+    }),
+    slots: ["default"],
+    description:
+      "Wrapper for post-processing effects. Add effects as children.",
+    example: { multisampling: 8 },
+  },
+
+  Bloom: {
+    props: z.object({
+      intensity: z.number().nullable(),
+      luminanceThreshold: z.number().nullable(),
+      luminanceSmoothing: z.number().nullable(),
+      mipmapBlur: z.boolean().nullable(),
+    }),
+    description: "Bloom post-processing effect. Makes emissive materials glow.",
+    example: { intensity: 1.5, luminanceThreshold: 0.1, mipmapBlur: true },
+  },
+
+  Glitch: {
+    props: z.object({
+      delay: z.tuple([z.number(), z.number()]).nullable(),
+      duration: z.tuple([z.number(), z.number()]).nullable(),
+      strength: z.tuple([z.number(), z.number()]).nullable(),
+      active: z.boolean().nullable(),
+      ratio: z.number().nullable(),
+    }),
+    description: "Cyberpunk glitch post-processing effect.",
+    example: { active: true },
+  },
+
+  Vignette: {
+    props: z.object({
+      offset: z.number().nullable(),
+      darkness: z.number().nullable(),
+    }),
+    description: "Vignette post-processing effect (darkened corners).",
+    example: { offset: 0.5, darkness: 0.5 },
+  },
+
+  // ===========================================================================
+  // Animation Wrappers
+  // ===========================================================================
+
+  WarpTunnel: {
+    props: z.object({
+      ...transformProps,
+      ringCount: z.number().nullable(),
+      radius: z.number().nullable(),
+      length: z.number().nullable(),
+      speed: z.number().nullable(),
+      tubeRadius: z.number().nullable(),
+      color1: z.string().nullable(),
+      color2: z.string().nullable(),
+    }),
+    description:
+      "Animated neon tunnel flythrough. Rings rush toward the camera creating a hyperspace warp effect.",
+    example: {
+      speed: 8,
+      ringCount: 80,
+      radius: 3,
+      color1: "#00ffff",
+      color2: "#ff00ff",
+    },
+  },
+
+  Spin: {
+    props: z.object({
+      ...transformProps,
+      speed: z.number().nullable(),
+      axis: z.enum(["x", "y", "z"]).nullable(),
+    }),
+    slots: ["default"],
+    description:
+      "Wrapper that continuously spins children around an axis. Speed in radians/second.",
+    example: { speed: 1, axis: "y" },
+  },
+
+  Orbit: {
+    props: z.object({
+      ...transformProps,
+      speed: z.number().nullable(),
+      radius: z.number().nullable(),
+      tilt: z.number().nullable(),
+    }),
+    slots: ["default"],
+    description:
+      "Wrapper that orbits children around the Y axis at a given radius and speed.",
+    example: { speed: 1, radius: 3, tilt: 0.5 },
+  },
+
+  Pulse: {
+    props: z.object({
+      ...transformProps,
+      speed: z.number().nullable(),
+      min: z.number().nullable(),
+      max: z.number().nullable(),
+    }),
+    slots: ["default"],
+    description:
+      "Wrapper that pulses children's scale between min and max values.",
+    example: { speed: 1, min: 0.8, max: 1.2 },
+  },
+
+  CameraShake: {
+    props: z.object({
+      intensity: z.number().nullable(),
+      maxYaw: z.number().nullable(),
+      maxPitch: z.number().nullable(),
+      maxRoll: z.number().nullable(),
+    }),
+    description:
+      "Adds camera shake/vibration. Great for speed, impact, or unease effects.",
+    example: { intensity: 0.5, maxYaw: 0.1, maxPitch: 0.1, maxRoll: 0.1 },
+  },
+
+  // ===========================================================================
+  // Camera / Controls
+  // ===========================================================================
+
+  PerspectiveCamera: {
+    props: z.object({
+      ...transformProps,
+      fov: z.number().nullable(),
+      near: z.number().nullable(),
+      far: z.number().nullable(),
+      makeDefault: z.boolean().nullable(),
+    }),
+    description:
+      "Perspective camera. Set makeDefault to use as the scene's main camera.",
+    example: {
+      position: [5, 5, 5],
+      fov: 50,
+      makeDefault: true,
+    },
+  },
+
+  OrbitControls: {
+    props: z.object({
+      enableDamping: z.boolean().nullable(),
+      dampingFactor: z.number().nullable(),
+      enableZoom: z.boolean().nullable(),
+      enablePan: z.boolean().nullable(),
+      enableRotate: z.boolean().nullable(),
+      minDistance: z.number().nullable(),
+      maxDistance: z.number().nullable(),
+      minPolarAngle: z.number().nullable(),
+      maxPolarAngle: z.number().nullable(),
+      autoRotate: z.boolean().nullable(),
+      autoRotateSpeed: z.number().nullable(),
+      target: z.tuple([z.number(), z.number(), z.number()]).nullable(),
+    }),
+    description:
+      "Orbit camera controls. Allows the user to rotate, zoom, and pan around the scene.",
+    example: { enableDamping: true, autoRotate: false },
+  },
+};
+
+// =============================================================================
+// Types
+// =============================================================================
+
+export type ComponentDefinition = {
+  props: z.ZodType;
+  slots?: string[];
+  description: string;
+  example?: Record<string, unknown>;
+};
+
+/**
+ * Infer the props type for a Three component by name.
+ *
+ * @example
+ * ```ts
+ * type BoxProps = ThreeProps<"Box">;
+ * ```
+ */
+export type ThreeProps<K extends keyof typeof threeComponentDefinitions> =
+  z.output<(typeof threeComponentDefinitions)[K]["props"]>;

+ 909 - 0
packages/react-three-fiber/src/components.tsx

@@ -0,0 +1,909 @@
+// @ts-nocheck
+import React, { type ReactNode, Suspense, useRef, useMemo } from "react";
+import { useFrame } from "@react-three/fiber";
+import {
+  Environment as DreiEnvironment,
+  OrbitControls as DreiOrbitControls,
+  PerspectiveCamera as DreiPerspectiveCamera,
+  Text as DreiText,
+  Gltf,
+  Sparkles as DreiSparkles,
+  Stars as DreiStars,
+  Sky as DreiSky,
+  Cloud as DreiCloud,
+  Clouds as DreiClouds,
+  Float as DreiFloat,
+  ContactShadows as DreiContactShadows,
+  MeshTransmissionMaterial,
+  MeshDistortMaterial,
+  MeshReflectorMaterial,
+  MeshPortalMaterial as DreiMeshPortalMaterial,
+  Html as DreiHtml,
+  RoundedBox as DreiRoundedBox,
+  Backdrop as DreiBackdrop,
+  CameraShake as DreiCameraShake,
+} from "@react-three/drei";
+// @ts-ignore
+import * as postprocessing from "@react-three/postprocessing";
+import * as THREE from "three";
+import type { BaseComponentProps } from "@json-render/react";
+import type { ThreeProps } from "./catalog";
+
+// =============================================================================
+// Helpers
+// =============================================================================
+
+type Vec3 = [number, number, number];
+
+const DEFAULT_POS: Vec3 = [0, 0, 0];
+const DEFAULT_ROT: Vec3 = [0, 0, 0];
+const DEFAULT_SCALE: Vec3 = [1, 1, 1];
+
+function pos(v: Vec3 | null | undefined): Vec3 {
+  return v ?? DEFAULT_POS;
+}
+
+function rot(v: Vec3 | null | undefined): Vec3 {
+  return v ?? DEFAULT_ROT;
+}
+
+function scl(v: Vec3 | null | undefined): Vec3 {
+  return v ?? DEFAULT_SCALE;
+}
+
+function MaterialComponent({
+  material,
+}: {
+  material: ThreeProps<"Box">["material"] | null | undefined;
+}) {
+  if (!material) return <meshStandardMaterial />;
+  return (
+    <meshStandardMaterial
+      color={material.color ?? "#ffffff"}
+      metalness={material.metalness ?? 0}
+      roughness={material.roughness ?? 1}
+      emissive={material.emissive ?? "#000000"}
+      emissiveIntensity={material.emissiveIntensity ?? 1}
+      opacity={material.opacity ?? 1}
+      transparent={material.transparent ?? false}
+      wireframe={material.wireframe ?? false}
+    />
+  );
+}
+
+// =============================================================================
+// Component Implementations
+// =============================================================================
+
+/**
+ * React Three Fiber component implementations for json-render.
+ *
+ * Pass to `defineRegistry()` from `@json-render/react` to create a
+ * component registry for rendering JSON specs as 3D scenes.
+ *
+ * @example
+ * ```ts
+ * import { defineRegistry } from "@json-render/react";
+ * import { threeComponents } from "@json-render/react-three-fiber";
+ *
+ * const { registry } = defineRegistry(catalog, {
+ *   components: { ...threeComponents },
+ * });
+ * ```
+ */
+export const threeComponents = {
+  // ── Primitives ─────────────────────────────────────────────────────────
+
+  Box: ({ props }: BaseComponentProps<ThreeProps<"Box">>) => (
+    <mesh
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      castShadow={props.castShadow ?? false}
+      receiveShadow={props.receiveShadow ?? false}
+    >
+      <boxGeometry
+        args={[props.width ?? 1, props.height ?? 1, props.depth ?? 1]}
+      />
+      <MaterialComponent material={props.material} />
+    </mesh>
+  ),
+
+  Sphere: ({ props }: BaseComponentProps<ThreeProps<"Sphere">>) => (
+    <mesh
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      castShadow={props.castShadow ?? false}
+      receiveShadow={props.receiveShadow ?? false}
+    >
+      <sphereGeometry
+        args={[
+          props.radius ?? 1,
+          props.widthSegments ?? 32,
+          props.heightSegments ?? 16,
+        ]}
+      />
+      <MaterialComponent material={props.material} />
+    </mesh>
+  ),
+
+  Cylinder: ({ props }: BaseComponentProps<ThreeProps<"Cylinder">>) => (
+    <mesh
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      castShadow={props.castShadow ?? false}
+      receiveShadow={props.receiveShadow ?? false}
+    >
+      <cylinderGeometry
+        args={[
+          props.radiusTop ?? 1,
+          props.radiusBottom ?? 1,
+          props.height ?? 1,
+          props.radialSegments ?? 32,
+        ]}
+      />
+      <MaterialComponent material={props.material} />
+    </mesh>
+  ),
+
+  Cone: ({ props }: BaseComponentProps<ThreeProps<"Cone">>) => (
+    <mesh
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      castShadow={props.castShadow ?? false}
+      receiveShadow={props.receiveShadow ?? false}
+    >
+      <coneGeometry
+        args={[
+          props.radius ?? 1,
+          props.height ?? 1,
+          props.radialSegments ?? 32,
+        ]}
+      />
+      <MaterialComponent material={props.material} />
+    </mesh>
+  ),
+
+  Torus: ({ props }: BaseComponentProps<ThreeProps<"Torus">>) => (
+    <mesh
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      castShadow={props.castShadow ?? false}
+      receiveShadow={props.receiveShadow ?? false}
+    >
+      <torusGeometry
+        args={[
+          props.radius ?? 1,
+          props.tube ?? 0.4,
+          props.radialSegments ?? 16,
+          props.tubularSegments ?? 48,
+        ]}
+      />
+      <MaterialComponent material={props.material} />
+    </mesh>
+  ),
+
+  Plane: ({
+    props,
+    children,
+  }: BaseComponentProps<ThreeProps<"Plane">> & { children?: ReactNode }) => (
+    <mesh
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      castShadow={props.castShadow ?? false}
+      receiveShadow={props.receiveShadow ?? false}
+    >
+      <planeGeometry args={[props.width ?? 1, props.height ?? 1]} />
+      {children ?? <MaterialComponent material={props.material} />}
+    </mesh>
+  ),
+
+  Capsule: ({ props }: BaseComponentProps<ThreeProps<"Capsule">>) => (
+    <mesh
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      castShadow={props.castShadow ?? false}
+      receiveShadow={props.receiveShadow ?? false}
+    >
+      <capsuleGeometry
+        args={[
+          props.radius ?? 0.5,
+          props.length ?? 1,
+          props.capSegments ?? 4,
+          props.radialSegments ?? 16,
+        ]}
+      />
+      <MaterialComponent material={props.material} />
+    </mesh>
+  ),
+
+  // ── Lights ─────────────────────────────────────────────────────────────
+
+  AmbientLight: ({ props }: BaseComponentProps<ThreeProps<"AmbientLight">>) => (
+    <ambientLight
+      color={props.color ?? "#ffffff"}
+      intensity={props.intensity ?? 1}
+    />
+  ),
+
+  DirectionalLight: ({
+    props,
+  }: BaseComponentProps<ThreeProps<"DirectionalLight">>) => (
+    <directionalLight
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      color={props.color ?? "#ffffff"}
+      intensity={props.intensity ?? 1}
+      castShadow={props.castShadow ?? false}
+    />
+  ),
+
+  PointLight: ({ props }: BaseComponentProps<ThreeProps<"PointLight">>) => (
+    <pointLight
+      position={pos(props.position)}
+      color={props.color ?? "#ffffff"}
+      intensity={props.intensity ?? 1}
+      distance={props.distance ?? 0}
+      decay={props.decay ?? 2}
+      castShadow={props.castShadow ?? false}
+    />
+  ),
+
+  SpotLight: ({ props }: BaseComponentProps<ThreeProps<"SpotLight">>) => (
+    <spotLight
+      position={pos(props.position)}
+      color={props.color ?? "#ffffff"}
+      intensity={props.intensity ?? 1}
+      distance={props.distance ?? 0}
+      decay={props.decay ?? 2}
+      angle={props.angle ?? Math.PI / 3}
+      penumbra={props.penumbra ?? 0}
+      castShadow={props.castShadow ?? false}
+    />
+  ),
+
+  // ── Container ──────────────────────────────────────────────────────────
+
+  Group: ({
+    props,
+    children,
+  }: BaseComponentProps<ThreeProps<"Group">> & { children?: ReactNode }) => (
+    <group
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+    >
+      {children}
+    </group>
+  ),
+
+  // ── Model ──────────────────────────────────────────────────────────────
+
+  Model: ({ props }: BaseComponentProps<ThreeProps<"Model">>) => (
+    <Suspense fallback={null}>
+      <Gltf
+        src={props.url}
+        position={pos(props.position)}
+        rotation={rot(props.rotation)}
+        scale={scl(props.scale)}
+        castShadow={props.castShadow ?? false}
+        receiveShadow={props.receiveShadow ?? false}
+      />
+    </Suspense>
+  ),
+
+  // ── Environment ────────────────────────────────────────────────────────
+
+  Environment: ({ props }: BaseComponentProps<ThreeProps<"Environment">>) => (
+    <DreiEnvironment
+      preset={props.preset ?? "sunset"}
+      background={props.background ?? false}
+      blur={props.blur ?? 0}
+      environmentIntensity={props.intensity ?? 1}
+    />
+  ),
+
+  Fog: ({ props }: BaseComponentProps<ThreeProps<"Fog">>) => (
+    <fog
+      attach="fog"
+      args={[props.color ?? "#cccccc", props.near ?? 10, props.far ?? 50]}
+    />
+  ),
+
+  GridHelper: ({ props }: BaseComponentProps<ThreeProps<"GridHelper">>) => (
+    <group
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+    >
+      <gridHelper
+        args={[
+          props.size ?? 10,
+          props.divisions ?? 10,
+          props.color ?? "#888888",
+          props.secondaryColor ?? "#444444",
+        ]}
+      />
+    </group>
+  ),
+
+  // ── Text ───────────────────────────────────────────────────────────────
+
+  Text3D: ({ props }: BaseComponentProps<ThreeProps<"Text3D">>) => (
+    <DreiText
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      fontSize={props.fontSize ?? 1}
+      color={props.color ?? "#ffffff"}
+      anchorX={props.anchorX ?? "center"}
+      anchorY={props.anchorY ?? "middle"}
+      maxWidth={props.maxWidth ?? undefined}
+    >
+      {props.text}
+    </DreiText>
+  ),
+
+  // ── Effects / Atmosphere ──────────────────────────────────────────────
+
+  Sparkles: ({ props }: BaseComponentProps<ThreeProps<"Sparkles">>) => (
+    <DreiSparkles
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      count={props.count ?? 50}
+      speed={props.speed ?? 0.4}
+      opacity={props.opacity ?? 1}
+      color={props.color ?? "#ffffff"}
+      size={props.size ?? 1}
+      noise={props.noise ?? 1}
+    />
+  ),
+
+  Stars: ({ props }: BaseComponentProps<ThreeProps<"Stars">>) => (
+    <DreiStars
+      radius={props.radius ?? 100}
+      depth={props.depth ?? 50}
+      count={props.count ?? 5000}
+      factor={props.factor ?? 4}
+      saturation={props.saturation ?? 0}
+      fade={props.fade ?? true}
+      speed={props.speed ?? 1}
+    />
+  ),
+
+  Sky: ({ props }: BaseComponentProps<ThreeProps<"Sky">>) => (
+    <DreiSky
+      distance={props.distance ?? 450000}
+      sunPosition={props.sunPosition ?? [0, 1, 0]}
+      inclination={props.inclination ?? undefined}
+      azimuth={props.azimuth ?? undefined}
+      mieCoefficient={props.mieCoefficient ?? 0.005}
+      mieDirectionalG={props.mieDirectionalG ?? 0.8}
+      rayleigh={props.rayleigh ?? 0.5}
+      turbidity={props.turbidity ?? 10}
+    />
+  ),
+
+  Cloud: ({ props }: BaseComponentProps<ThreeProps<"Cloud">>) => (
+    <DreiClouds limit={200}>
+      <DreiCloud
+        position={pos(props.position)}
+        seed={props.seed ?? undefined}
+        segments={props.segments ?? 20}
+        bounds={props.bounds ?? [10, 2, 10]}
+        volume={props.volume ?? 6}
+        speed={props.speed ?? 0.2}
+        fade={props.fade ?? 30}
+        opacity={props.opacity ?? 0.6}
+        color={props.color ?? "#ffffff"}
+        growth={props.growth ?? 4}
+      />
+    </DreiClouds>
+  ),
+
+  // ── Special Materials ───────────────────────────────────────────────
+
+  GlassSphere: ({ props }: BaseComponentProps<ThreeProps<"GlassSphere">>) => (
+    <mesh
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      castShadow={props.castShadow ?? false}
+      receiveShadow={props.receiveShadow ?? false}
+    >
+      <sphereGeometry
+        args={[
+          props.radius ?? 1,
+          props.widthSegments ?? 64,
+          props.heightSegments ?? 32,
+        ]}
+      />
+      <MeshTransmissionMaterial
+        color={props.color ?? "#ffffff"}
+        transmission={props.transmission ?? 1}
+        thickness={props.thickness ?? 0.5}
+        roughness={props.roughness ?? 0}
+        chromaticAberration={props.chromaticAberration ?? 0.06}
+        ior={props.ior ?? 1.5}
+        distortion={props.distortion ?? 0}
+        distortionScale={props.distortionScale ?? 0.3}
+        temporalDistortion={props.temporalDistortion ?? 0.5}
+        samples={props.samples ?? 10}
+        resolution={props.resolution ?? 256}
+        backside
+      />
+    </mesh>
+  ),
+
+  GlassBox: ({ props }: BaseComponentProps<ThreeProps<"GlassBox">>) => (
+    <mesh
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      castShadow={props.castShadow ?? false}
+      receiveShadow={props.receiveShadow ?? false}
+    >
+      <boxGeometry
+        args={[props.width ?? 1, props.height ?? 1, props.depth ?? 1]}
+      />
+      <MeshTransmissionMaterial
+        color={props.color ?? "#ffffff"}
+        transmission={props.transmission ?? 1}
+        thickness={props.thickness ?? 0.5}
+        roughness={props.roughness ?? 0}
+        chromaticAberration={props.chromaticAberration ?? 0.06}
+        ior={props.ior ?? 1.5}
+        distortion={props.distortion ?? 0}
+        distortionScale={props.distortionScale ?? 0.3}
+        temporalDistortion={props.temporalDistortion ?? 0.5}
+        samples={props.samples ?? 10}
+        resolution={props.resolution ?? 256}
+        backside
+      />
+    </mesh>
+  ),
+
+  DistortSphere: ({
+    props,
+  }: BaseComponentProps<ThreeProps<"DistortSphere">>) => (
+    <mesh
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      castShadow={props.castShadow ?? false}
+      receiveShadow={props.receiveShadow ?? false}
+    >
+      <sphereGeometry
+        args={[
+          props.radius ?? 1,
+          props.widthSegments ?? 64,
+          props.heightSegments ?? 32,
+        ]}
+      />
+      <MeshDistortMaterial
+        color={props.color ?? "#ff6600"}
+        speed={props.speed ?? 2}
+        distort={props.distort ?? 0.5}
+        metalness={props.metalness ?? 0}
+        roughness={props.roughness ?? 0.2}
+      />
+    </mesh>
+  ),
+
+  // ── Extended Geometry ───────────────────────────────────────────────
+
+  TorusKnot: ({ props }: BaseComponentProps<ThreeProps<"TorusKnot">>) => (
+    <mesh
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      castShadow={props.castShadow ?? false}
+      receiveShadow={props.receiveShadow ?? false}
+    >
+      <torusKnotGeometry
+        args={[
+          props.radius ?? 1,
+          props.tube ?? 0.4,
+          props.tubularSegments ?? 64,
+          props.radialSegments ?? 8,
+          props.p ?? 2,
+          props.q ?? 3,
+        ]}
+      />
+      <MaterialComponent material={props.material} />
+    </mesh>
+  ),
+
+  RoundedBox: ({ props }: BaseComponentProps<ThreeProps<"RoundedBox">>) => (
+    <DreiRoundedBox
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      castShadow={props.castShadow ?? false}
+      receiveShadow={props.receiveShadow ?? false}
+      args={[props.width ?? 1, props.height ?? 1, props.depth ?? 1]}
+      radius={props.radius ?? 0.05}
+      smoothness={props.smoothness ?? 4}
+    >
+      <MaterialComponent material={props.material} />
+    </DreiRoundedBox>
+  ),
+
+  // ── Shadows / Staging ───────────────────────────────────────────────
+
+  ContactShadows: ({
+    props,
+  }: BaseComponentProps<ThreeProps<"ContactShadows">>) => (
+    <DreiContactShadows
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      opacity={props.opacity ?? 0.5}
+      width={props.width ?? 10}
+      height={props.height ?? 10}
+      blur={props.blur ?? 2}
+      near={props.near ?? undefined}
+      far={props.far ?? 10}
+      smooth={props.smooth ?? true}
+      resolution={props.resolution ?? 256}
+      frames={props.frames ?? undefined}
+      color={props.color ?? "#000000"}
+    />
+  ),
+
+  Float: ({
+    props,
+    children,
+  }: BaseComponentProps<ThreeProps<"Float">> & { children?: ReactNode }) => (
+    <DreiFloat
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      speed={props.speed ?? 1}
+      rotationIntensity={props.rotationIntensity ?? 1}
+      floatIntensity={props.floatIntensity ?? 1}
+      enabled={props.enabled ?? true}
+    >
+      {children}
+    </DreiFloat>
+  ),
+
+  ReflectorPlane: ({
+    props,
+  }: BaseComponentProps<ThreeProps<"ReflectorPlane">>) => (
+    <mesh
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+    >
+      <planeGeometry args={[props.width ?? 10, props.height ?? 10]} />
+      <MeshReflectorMaterial
+        color={props.color ?? "#888888"}
+        resolution={props.resolution ?? 1024}
+        blur={props.blur ? [props.blur, props.blur] : [300, 100]}
+        mirror={props.mirror ?? 0.5}
+        mixBlur={props.mixBlur ?? 10}
+        mixStrength={props.mixStrength ?? 2}
+        depthScale={props.depthScale ?? 0.1}
+        metalness={props.metalness ?? 0.5}
+        roughness={props.roughness ?? 1}
+      />
+    </mesh>
+  ),
+
+  Backdrop: ({
+    props,
+    children,
+  }: BaseComponentProps<ThreeProps<"Backdrop">> & { children?: ReactNode }) => (
+    <DreiBackdrop
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      scale={scl(props.scale)}
+      floor={props.floor ?? 0.25}
+      segments={props.segments ?? 20}
+      receiveShadow={props.receiveShadow ?? true}
+    >
+      {children}
+    </DreiBackdrop>
+  ),
+
+  // ── Crazy / Magic ─────────────────────────────────────────────────────
+
+  MeshPortalMaterial: ({
+    props,
+    children,
+  }: BaseComponentProps<ThreeProps<"MeshPortalMaterial">> & {
+    children?: ReactNode;
+  }) => (
+    <DreiMeshPortalMaterial
+      blend={props.blend ?? 0}
+      blur={props.blur ?? 0}
+      resolution={props.resolution ?? 512}
+    >
+      {children}
+    </DreiMeshPortalMaterial>
+  ),
+
+  HtmlLabel: ({ props }: BaseComponentProps<ThreeProps<"HtmlLabel">>) => (
+    <DreiHtml
+      position={pos(props.position)}
+      transform={props.transform ?? true}
+      distanceFactor={props.distanceFactor ?? 10}
+      center={props.center ?? true}
+      style={{
+        color: props.color ?? "#ffffff",
+        fontSize: props.fontSize ? `${props.fontSize}px` : "16px",
+        fontFamily: "system-ui, sans-serif",
+        whiteSpace: "nowrap",
+        pointerEvents: "none",
+      }}
+    >
+      {props.text}
+    </DreiHtml>
+  ),
+
+  // ── Post-Processing ────────────────────────────────────────────────────
+
+  EffectComposer: ({
+    props,
+    children,
+  }: BaseComponentProps<ThreeProps<"EffectComposer">> & {
+    children?: ReactNode;
+  }) => {
+    if (props.enabled === false) return null;
+    return (
+      <postprocessing.EffectComposer multisampling={props.multisampling ?? 8}>
+        {children}
+      </postprocessing.EffectComposer>
+    );
+  },
+
+  Bloom: ({ props }: BaseComponentProps<ThreeProps<"Bloom">>) => (
+    <postprocessing.Bloom
+      intensity={props.intensity ?? 1.5}
+      luminanceThreshold={props.luminanceThreshold ?? 0.1}
+      luminanceSmoothing={props.luminanceSmoothing ?? 0.025}
+      mipmapBlur={props.mipmapBlur ?? true}
+    />
+  ),
+
+  Glitch: ({ props }: BaseComponentProps<ThreeProps<"Glitch">>) => {
+    const delay = props.delay ? new THREE.Vector2(...props.delay) : undefined;
+    const duration = props.duration
+      ? new THREE.Vector2(...props.duration)
+      : undefined;
+    const strength = props.strength
+      ? new THREE.Vector2(...props.strength)
+      : undefined;
+    return (
+      <postprocessing.Glitch
+        delay={delay}
+        duration={duration}
+        strength={strength}
+        active={props.active ?? true}
+        ratio={props.ratio ?? 0.85}
+      />
+    );
+  },
+
+  Vignette: ({ props }: BaseComponentProps<ThreeProps<"Vignette">>) => (
+    <postprocessing.Vignette
+      offset={props.offset ?? 0.5}
+      darkness={props.darkness ?? 0.5}
+    />
+  ),
+
+  // ── Animation Wrappers ──────────────────────────────────────────────
+
+  WarpTunnel: (() => {
+    function TunnelRing({
+      index,
+      ringCount,
+      radius,
+      length,
+      speed,
+      tubeRadius,
+      color,
+    }: {
+      index: number;
+      ringCount: number;
+      radius: number;
+      length: number;
+      speed: number;
+      tubeRadius: number;
+      color: THREE.Color;
+    }) {
+      const meshRef = useRef<THREE.Mesh>(null);
+      const spacing = length / ringCount;
+
+      useFrame((state) => {
+        if (!meshRef.current) return;
+        const t = state.clock.elapsedTime;
+        const z =
+          ((((-index * spacing + t * speed) % length) + length) % length) -
+          length;
+        meshRef.current.position.z = z;
+        const distFactor = 1 - Math.abs(z) / length;
+        const s = 0.3 + distFactor * 0.7;
+        meshRef.current.scale.set(s, s, s);
+      });
+
+      return (
+        <mesh ref={meshRef} rotation={[0, 0, (index * Math.PI) / 12]}>
+          <torusGeometry args={[radius, tubeRadius, 16, 64]} />
+          <meshStandardMaterial
+            color={color}
+            emissive={color}
+            emissiveIntensity={3}
+            transparent
+            opacity={0.7}
+            side={THREE.DoubleSide}
+          />
+        </mesh>
+      );
+    }
+
+    return ({ props }: BaseComponentProps<ThreeProps<"WarpTunnel">>) => {
+      const ringCount = props.ringCount ?? 80;
+      const radius = props.radius ?? 3;
+      const length = props.length ?? 40;
+      const speed = props.speed ?? 8;
+      const tubeRadius = props.tubeRadius ?? 0.02;
+      const color1 = props.color1 ?? "#00ffff";
+      const color2 = props.color2 ?? "#ff00ff";
+
+      const colors = useMemo(() => {
+        const c1 = new THREE.Color(color1);
+        const c2 = new THREE.Color(color2);
+        return Array.from({ length: ringCount }, (_, i) =>
+          new THREE.Color().lerpColors(c1, c2, i / ringCount),
+        );
+      }, [ringCount, color1, color2]);
+
+      return (
+        <group
+          position={pos(props.position)}
+          rotation={rot(props.rotation)}
+          scale={scl(props.scale)}
+        >
+          {colors.map((color, i) => (
+            <TunnelRing
+              key={i}
+              index={i}
+              ringCount={ringCount}
+              radius={radius}
+              length={length}
+              speed={speed}
+              tubeRadius={tubeRadius}
+              color={color}
+            />
+          ))}
+        </group>
+      );
+    };
+  })(),
+
+  Spin: ({
+    props,
+    children,
+  }: BaseComponentProps<ThreeProps<"Spin">> & { children?: ReactNode }) => {
+    const ref = useRef<THREE.Group>(null);
+    const speed = props.speed ?? 1;
+    const axis = props.axis ?? "y";
+
+    useFrame((_, delta) => {
+      if (!ref.current) return;
+      if (axis === "x") ref.current.rotation.x += delta * speed;
+      else if (axis === "z") ref.current.rotation.z += delta * speed;
+      else ref.current.rotation.y += delta * speed;
+    });
+
+    return (
+      <group
+        ref={ref}
+        position={pos(props.position)}
+        rotation={rot(props.rotation)}
+        scale={scl(props.scale)}
+      >
+        {children}
+      </group>
+    );
+  },
+
+  Orbit: ({
+    props,
+    children,
+  }: BaseComponentProps<ThreeProps<"Orbit">> & { children?: ReactNode }) => {
+    const ref = useRef<THREE.Group>(null);
+    const speed = props.speed ?? 1;
+    const radius = props.radius ?? 3;
+    const tilt = props.tilt ?? 0;
+    const baseY = props.position?.[1] ?? 0;
+
+    useFrame((state) => {
+      if (!ref.current) return;
+      const t = state.clock.elapsedTime * speed;
+      ref.current.position.x = Math.cos(t) * radius;
+      ref.current.position.z = Math.sin(t) * radius;
+      ref.current.position.y = baseY + Math.sin(t * 0.7) * tilt;
+    });
+
+    return <group ref={ref}>{children}</group>;
+  },
+
+  Pulse: ({
+    props,
+    children,
+  }: BaseComponentProps<ThreeProps<"Pulse">> & { children?: ReactNode }) => {
+    const ref = useRef<THREE.Group>(null);
+    const speed = props.speed ?? 1;
+    const min = props.min ?? 0.8;
+    const max = props.max ?? 1.2;
+
+    useFrame((state) => {
+      if (!ref.current) return;
+      const t = (Math.sin(state.clock.elapsedTime * speed) + 1) / 2;
+      const s = min + t * (max - min);
+      ref.current.scale.setScalar(s);
+    });
+
+    return (
+      <group
+        ref={ref}
+        position={pos(props.position)}
+        rotation={rot(props.rotation)}
+      >
+        {children}
+      </group>
+    );
+  },
+
+  CameraShake: ({ props }: BaseComponentProps<ThreeProps<"CameraShake">>) => (
+    <DreiCameraShake
+      intensity={props.intensity ?? 0.5}
+      maxYaw={props.maxYaw ?? 0.1}
+      maxPitch={props.maxPitch ?? 0.1}
+      maxRoll={props.maxRoll ?? 0.1}
+    />
+  ),
+
+  // ── Camera / Controls ──────────────────────────────────────────────
+
+  PerspectiveCamera: ({
+    props,
+  }: BaseComponentProps<ThreeProps<"PerspectiveCamera">>) => (
+    <DreiPerspectiveCamera
+      position={pos(props.position)}
+      rotation={rot(props.rotation)}
+      fov={props.fov ?? 50}
+      near={props.near ?? 0.1}
+      far={props.far ?? 1000}
+      makeDefault={props.makeDefault ?? false}
+    />
+  ),
+
+  OrbitControls: ({
+    props,
+  }: BaseComponentProps<ThreeProps<"OrbitControls">>) => (
+    <DreiOrbitControls
+      enableDamping={props.enableDamping ?? true}
+      dampingFactor={props.dampingFactor ?? 0.05}
+      enableZoom={props.enableZoom ?? true}
+      enablePan={props.enablePan ?? true}
+      enableRotate={props.enableRotate ?? true}
+      minDistance={props.minDistance ?? undefined}
+      maxDistance={props.maxDistance ?? undefined}
+      minPolarAngle={props.minPolarAngle ?? undefined}
+      maxPolarAngle={props.maxPolarAngle ?? undefined}
+      autoRotate={props.autoRotate ?? false}
+      autoRotateSpeed={props.autoRotateSpeed ?? 2}
+      target={props.target ?? undefined}
+    />
+  ),
+};

+ 25 - 0
packages/react-three-fiber/src/index.ts

@@ -0,0 +1,25 @@
+// Component implementations
+export { threeComponents } from "./components";
+
+// Catalog definitions (also available via @json-render/react-three-fiber/catalog)
+export {
+  threeComponentDefinitions,
+  type ComponentDefinition,
+  type ThreeProps,
+} from "./catalog";
+
+// Shared schemas
+export {
+  vector3Schema,
+  materialSchema,
+  transformProps,
+  shadowProps,
+} from "./schemas";
+
+// Renderer
+export {
+  ThreeRenderer,
+  ThreeCanvas,
+  type ThreeRendererProps,
+  type ThreeCanvasProps,
+} from "./renderer";

+ 19 - 0
packages/react-three-fiber/src/r3f-jsx.d.ts

@@ -0,0 +1,19 @@
+import type { ThreeElements } from "@react-three/fiber";
+
+declare module "react/jsx-runtime" {
+  namespace JSX {
+    interface IntrinsicElements extends ThreeElements {}
+  }
+}
+
+declare module "react/jsx-dev-runtime" {
+  namespace JSX {
+    interface IntrinsicElements extends ThreeElements {}
+  }
+}
+
+declare module "react" {
+  namespace JSX {
+    interface IntrinsicElements extends ThreeElements {}
+  }
+}

+ 144 - 0
packages/react-three-fiber/src/renderer.tsx

@@ -0,0 +1,144 @@
+import { type ReactNode } from "react";
+import { Canvas, type CanvasProps } from "@react-three/fiber";
+import {
+  Renderer,
+  StateProvider,
+  VisibilityProvider,
+  ActionProvider,
+  ValidationProvider,
+  type ComponentRegistry,
+  type ComponentRenderer,
+} from "@json-render/react";
+import type { Spec, StateStore, ComputedFunction } from "@json-render/core";
+
+// =============================================================================
+// ThreeRenderer
+// =============================================================================
+
+export interface ThreeRendererProps {
+  /** The spec to render as a 3D scene */
+  spec: Spec | null;
+  /** Component registry */
+  registry: ComponentRegistry;
+  /**
+   * External store (controlled mode). When provided, `initialState` and
+   * `onStateChange` are ignored.
+   */
+  store?: StateStore;
+  /** Initial state model (uncontrolled mode) */
+  initialState?: Record<string, unknown>;
+  /** Action handlers */
+  handlers?: Record<
+    string,
+    (params: Record<string, unknown>) => Promise<unknown> | unknown
+  >;
+  /** Named functions for `$computed` expressions in props */
+  functions?: Record<string, ComputedFunction>;
+  /** Callback when state changes (uncontrolled mode) */
+  onStateChange?: (changes: Array<{ path: string; value: unknown }>) => void;
+  /** Whether the spec is currently loading/streaming */
+  loading?: boolean;
+  /** Fallback component for unknown types */
+  fallback?: ComponentRenderer;
+  /** Additional R3F elements to render alongside the spec (lights, helpers, etc.) */
+  children?: ReactNode;
+}
+
+/**
+ * Renders a json-render spec as a React Three Fiber scene graph.
+ *
+ * Place inside a `<Canvas>` or use `<ThreeCanvas>` for a convenience wrapper.
+ *
+ * @example
+ * ```tsx
+ * <Canvas shadows>
+ *   <ThreeRenderer spec={spec} registry={registry} />
+ * </Canvas>
+ * ```
+ */
+export function ThreeRenderer({
+  spec,
+  registry,
+  store,
+  initialState,
+  handlers,
+  functions,
+  onStateChange,
+  loading,
+  fallback,
+  children,
+}: ThreeRendererProps) {
+  return (
+    <StateProvider
+      store={store}
+      initialState={initialState ?? spec?.state}
+      onStateChange={onStateChange}
+    >
+      <VisibilityProvider>
+        <ValidationProvider>
+          <ActionProvider handlers={handlers}>
+            <Renderer
+              spec={spec}
+              registry={registry}
+              loading={loading}
+              fallback={fallback}
+            />
+            {children}
+          </ActionProvider>
+        </ValidationProvider>
+      </VisibilityProvider>
+    </StateProvider>
+  );
+}
+
+// =============================================================================
+// ThreeCanvas
+// =============================================================================
+
+export interface ThreeCanvasProps extends ThreeRendererProps {
+  /** Enable shadows on the Canvas */
+  shadows?: CanvasProps["shadows"];
+  /** Canvas camera config */
+  camera?: CanvasProps["camera"];
+  /** Canvas className */
+  className?: string;
+  /** Canvas style */
+  style?: React.CSSProperties;
+  /** Additional Canvas props */
+  canvasProps?: Omit<CanvasProps, "shadows" | "camera" | "className" | "style">;
+}
+
+/**
+ * Convenience wrapper that sets up a `<Canvas>` with a `<ThreeRenderer>` inside.
+ *
+ * @example
+ * ```tsx
+ * <ThreeCanvas
+ *   spec={spec}
+ *   registry={registry}
+ *   shadows
+ *   camera={{ position: [5, 5, 5], fov: 50 }}
+ *   style={{ width: "100%", height: "100vh" }}
+ * />
+ * ```
+ */
+export function ThreeCanvas({
+  shadows,
+  camera,
+  className,
+  style,
+  canvasProps,
+  ...rendererProps
+}: ThreeCanvasProps) {
+  return (
+    <Canvas
+      shadows={shadows}
+      camera={camera}
+      className={className}
+      style={style}
+      {...canvasProps}
+    >
+      <ThreeRenderer {...rendererProps} />
+    </Canvas>
+  );
+}

+ 25 - 0
packages/react-three-fiber/src/schemas.ts

@@ -0,0 +1,25 @@
+import { z } from "zod";
+
+export const vector3Schema = z.tuple([z.number(), z.number(), z.number()]);
+
+export const materialSchema = z.object({
+  color: z.string().nullable(),
+  metalness: z.number().nullable(),
+  roughness: z.number().nullable(),
+  emissive: z.string().nullable(),
+  emissiveIntensity: z.number().nullable(),
+  opacity: z.number().nullable(),
+  transparent: z.boolean().nullable(),
+  wireframe: z.boolean().nullable(),
+});
+
+export const transformProps = {
+  position: vector3Schema.nullable(),
+  rotation: vector3Schema.nullable(),
+  scale: vector3Schema.nullable(),
+} as const;
+
+export const shadowProps = {
+  castShadow: z.boolean().nullable(),
+  receiveShadow: z.boolean().nullable(),
+} as const;

+ 9 - 0
packages/react-three-fiber/tsconfig.json

@@ -0,0 +1,9 @@
+{
+  "extends": "@internal/typescript-config/react-library.json",
+  "compilerOptions": {
+    "outDir": "dist",
+    "rootDir": "src"
+  },
+  "include": ["src"],
+  "exclude": ["node_modules", "dist"]
+}

+ 19 - 0
packages/react-three-fiber/tsup.config.ts

@@ -0,0 +1,19 @@
+import { defineConfig } from "tsup";
+
+export default defineConfig({
+  entry: ["src/index.ts", "src/catalog.ts"],
+  format: ["cjs", "esm"],
+  dts: true,
+  sourcemap: true,
+  clean: true,
+  external: [
+    "react",
+    "react-dom",
+    "@json-render/core",
+    "@json-render/react",
+    "@react-three/fiber",
+    "@react-three/drei",
+    "three",
+    "zod",
+  ],
+});

+ 277 - 42
pnpm-lock.yaml

@@ -867,6 +867,61 @@ importers:
         specifier: ^5.7.2
         version: 5.9.3
 
+  examples/react-three-fiber:
+    dependencies:
+      '@json-render/core':
+        specifier: workspace:*
+        version: link:../../packages/core
+      '@json-render/react':
+        specifier: workspace:*
+        version: link:../../packages/react
+      '@json-render/react-three-fiber':
+        specifier: workspace:*
+        version: link:../../packages/react-three-fiber
+      '@react-three/drei':
+        specifier: ^10.7.7
+        version: 10.7.7(@react-three/fiber@9.5.0(@types/react@19.2.3)(expo-asset@12.0.12(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4)))(expo@54.0.33)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4)(three@0.183.2))(@types/react@19.2.3)(@types/three@0.183.1)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(three@0.183.2)
+      '@react-three/fiber':
+        specifier: ^9.5.0
+        version: 9.5.0(@types/react@19.2.3)(expo-asset@12.0.12(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4)))(expo@54.0.33)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4)(three@0.183.2)
+      next:
+        specifier: 16.1.6
+        version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
+      react:
+        specifier: 19.2.4
+        version: 19.2.4
+      react-dom:
+        specifier: 19.2.4
+        version: 19.2.4(react@19.2.4)
+      three:
+        specifier: ^0.183.2
+        version: 0.183.2
+      zod:
+        specifier: 4.3.6
+        version: 4.3.6
+    devDependencies:
+      '@internal/eslint-config':
+        specifier: workspace:*
+        version: link:../../packages/eslint-config
+      '@types/node':
+        specifier: ^22.10.0
+        version: 22.19.6
+      '@types/react':
+        specifier: 19.2.3
+        version: 19.2.3
+      '@types/react-dom':
+        specifier: 19.2.3
+        version: 19.2.3(@types/react@19.2.3)
+      '@types/three':
+        specifier: ^0.183.1
+        version: 0.183.1
+      eslint:
+        specifier: ^9.39.1
+        version: 9.39.2(jiti@2.6.1)
+      typescript:
+        specifier: ^5.7.2
+        version: 5.9.3
+
   examples/remotion:
     dependencies:
       '@ai-sdk/gateway':
@@ -1537,6 +1592,49 @@ importers:
         specifier: ^5.4.5
         version: 5.9.3
 
+  packages/react-three-fiber:
+    dependencies:
+      '@json-render/core':
+        specifier: workspace:*
+        version: link:../core
+      '@json-render/react':
+        specifier: workspace:*
+        version: link:../react
+      '@react-three/postprocessing':
+        specifier: ^3.0.4
+        version: 3.0.4(@react-three/fiber@9.5.0(@types/react@19.2.3)(expo-asset@12.0.12(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4)))(expo@54.0.33)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4)(three@0.183.2))(@types/three@0.183.1)(react@19.2.4)(three@0.183.2)
+      react:
+        specifier: ^19.0.0
+        version: 19.2.4
+    devDependencies:
+      '@internal/typescript-config':
+        specifier: workspace:*
+        version: link:../typescript-config
+      '@react-three/drei':
+        specifier: ^10.7.7
+        version: 10.7.7(@react-three/fiber@9.5.0(@types/react@19.2.3)(expo-asset@12.0.12(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4)))(expo@54.0.33)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4)(three@0.183.2))(@types/react@19.2.3)(@types/three@0.183.1)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(three@0.183.2)
+      '@react-three/fiber':
+        specifier: ^9.5.0
+        version: 9.5.0(@types/react@19.2.3)(expo-asset@12.0.12(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4)))(expo@54.0.33)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4)(three@0.183.2)
+      '@types/react':
+        specifier: 19.2.3
+        version: 19.2.3
+      '@types/three':
+        specifier: ^0.183.1
+        version: 0.183.1
+      three:
+        specifier: ^0.183.2
+        version: 0.183.2
+      tsup:
+        specifier: ^8.0.2
+        version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)
+      typescript:
+        specifier: ^5.4.5
+        version: 5.9.3
+      zod:
+        specifier: ^4.3.6
+        version: 4.3.6
+
   packages/redux:
     dependencies:
       '@json-render/core':
@@ -5133,6 +5231,13 @@ packages:
       react-native:
         optional: true
 
+  '@react-three/postprocessing@3.0.4':
+    resolution: {integrity: sha512-e4+F5xtudDYvhxx3y0NtWXpZbwvQ0x1zdOXWTbXMK6fFLVDd4qucN90YaaStanZGS4Bd5siQm0lGL/5ogf8iDQ==}
+    peerDependencies:
+      '@react-three/fiber': ^9.0.0
+      react: ^19.0
+      three: '>= 0.156.0'
+
   '@reduxjs/toolkit@2.11.2':
     resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==}
     peerDependencies:
@@ -6030,6 +6135,9 @@ packages:
   '@types/three@0.182.0':
     resolution: {integrity: sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==}
 
+  '@types/three@0.183.1':
+    resolution: {integrity: sha512-f2Pu5Hrepfgavttdye3PsH5RWyY/AvdZQwIVhrc4uNtvF7nOWJacQKcoVJn0S4f0yYbmAE6AR+ve7xDcuYtMGw==}
+
   '@types/trusted-types@2.0.7':
     resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
 
@@ -9709,6 +9817,12 @@ packages:
       '@types/three': '>=0.134.0'
       three: '>=0.134.0'
 
+  maath@0.6.0:
+    resolution: {integrity: sha512-dSb2xQuP7vDnaYqfoKzlApeRcR2xtN8/f7WV/TMAkBC8552TwTLtOO0JTcSygkYMjNDPoo6V01jTw/aPi4JrMw==}
+    peerDependencies:
+      '@types/three': '>=0.144.0'
+      three: '>=0.144.0'
+
   magic-string@0.30.21:
     resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
 
@@ -9837,6 +9951,9 @@ packages:
   meshoptimizer@0.22.0:
     resolution: {integrity: sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==}
 
+  meshoptimizer@1.0.1:
+    resolution: {integrity: sha512-Vix+QlA1YYT3FwmBBZ+49cE5y/b+pRrcXKqGpS5ouh33d3lSp2PoTpCw19E0cKDFWalembrHnIaZetf27a+W2g==}
+
   metro-babel-transformer@0.83.3:
     resolution: {integrity: sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g==}
     engines: {node: '>=20.19.4'}
@@ -10116,6 +10233,12 @@ packages:
   mz@2.7.0:
     resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
 
+  n8ao@1.10.1:
+    resolution: {integrity: sha512-hhI1pC+BfOZBV1KMwynBrVlIm8wqLxj/abAWhF2nZ0qQKyzTSQa1QtLVS2veRiuoBQXojxobcnp0oe+PUoxf/w==}
+    peerDependencies:
+      postprocessing: '>=6.30.0'
+      three: '>=0.137'
+
   nano-spawn@2.0.0:
     resolution: {integrity: sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==}
     engines: {node: '>=20.17'}
@@ -10624,6 +10747,11 @@ packages:
     resolution: {integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==}
     engines: {node: '>=12'}
 
+  postprocessing@6.38.3:
+    resolution: {integrity: sha512-5qCFp8j62nWL6sSVv/RKuHscQUIV+VMMgWeHLYZQEBpAk7G+r3jA3bSKON7gZjiuxdZ/F4PXj2Jc1oPh/7Eg+g==}
+    peerDependencies:
+      three: '>= 0.157.0 < 0.184.0'
+
   potpack@1.0.2:
     resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==}
 
@@ -11868,6 +11996,9 @@ packages:
   three@0.182.0:
     resolution: {integrity: sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==}
 
+  three@0.183.2:
+    resolution: {integrity: sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==}
+
   throat@5.0.0:
     resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==}
 
@@ -15120,7 +15251,7 @@ snapshots:
       '@mdx-js/mdx': 3.1.1
       source-map: 0.7.6
     optionalDependencies:
-      webpack: 5.96.1
+      webpack: 5.96.1(esbuild@0.25.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -15227,6 +15358,11 @@ snapshots:
       promise-worker-transferable: 1.0.4
       three: 0.182.0
 
+  '@monogrid/gainmap-js@3.4.0(three@0.183.2)':
+    dependencies:
+      promise-worker-transferable: 1.0.4
+      three: 0.183.2
+
   '@mswjs/interceptors@0.41.3':
     dependencies:
       '@open-draft/deferred-promise': 2.2.0
@@ -17642,6 +17778,39 @@ snapshots:
       - '@types/three'
       - immer
 
+  '@react-three/drei@10.7.7(@react-three/fiber@9.5.0(@types/react@19.2.3)(expo-asset@12.0.12(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4)))(expo@54.0.33)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4)(three@0.183.2))(@types/react@19.2.3)(@types/three@0.183.1)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(three@0.183.2)':
+    dependencies:
+      '@babel/runtime': 7.28.6
+      '@mediapipe/tasks-vision': 0.10.17
+      '@monogrid/gainmap-js': 3.4.0(three@0.183.2)
+      '@react-three/fiber': 9.5.0(@types/react@19.2.3)(expo-asset@12.0.12(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4)))(expo@54.0.33)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4)(three@0.183.2)
+      '@use-gesture/react': 10.3.1(react@19.2.4)
+      camera-controls: 3.1.2(three@0.183.2)
+      cross-env: 7.0.3
+      detect-gpu: 5.0.70
+      glsl-noise: 0.0.0
+      hls.js: 1.6.15
+      maath: 0.10.8(@types/three@0.183.1)(three@0.183.2)
+      meshline: 3.3.1(three@0.183.2)
+      react: 19.2.4
+      stats-gl: 2.4.2(@types/three@0.183.1)(three@0.183.2)
+      stats.js: 0.17.0
+      suspend-react: 0.1.3(react@19.2.4)
+      three: 0.183.2
+      three-mesh-bvh: 0.8.3(three@0.183.2)
+      three-stdlib: 2.36.1(three@0.183.2)
+      troika-three-text: 0.52.4(three@0.183.2)
+      tunnel-rat: 0.1.2(@types/react@19.2.3)(immer@11.1.4)(react@19.2.4)
+      use-sync-external-store: 1.6.0(react@19.2.4)
+      utility-types: 3.11.0
+      zustand: 5.0.11(@types/react@19.2.3)(immer@11.1.4)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))
+    optionalDependencies:
+      react-dom: 19.2.4(react@19.2.4)
+    transitivePeerDependencies:
+      - '@types/react'
+      - '@types/three'
+      - immer
+
   '@react-three/fiber@9.5.0(@types/react@19.2.3)(expo-asset@12.0.12(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4)))(expo@54.0.33)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4)(three@0.182.0)':
     dependencies:
       '@babel/runtime': 7.28.6
@@ -17666,6 +17835,41 @@ snapshots:
       - '@types/react'
       - immer
 
+  '@react-three/fiber@9.5.0(@types/react@19.2.3)(expo-asset@12.0.12(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4)))(expo@54.0.33)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4)(three@0.183.2)':
+    dependencies:
+      '@babel/runtime': 7.28.6
+      '@types/webxr': 0.5.24
+      base64-js: 1.5.1
+      buffer: 6.0.3
+      its-fine: 2.0.0(@types/react@19.2.3)(react@19.2.4)
+      react: 19.2.4
+      react-use-measure: 2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
+      scheduler: 0.27.0
+      suspend-react: 0.1.3(react@19.2.4)
+      three: 0.183.2
+      use-sync-external-store: 1.6.0(react@19.2.4)
+      zustand: 5.0.11(@types/react@19.2.3)(immer@11.1.4)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4))
+    optionalDependencies:
+      expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4)
+      expo-asset: 12.0.12(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4)
+      expo-file-system: 19.0.21(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))
+      react-dom: 19.2.4(react@19.2.4)
+      react-native: 0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4)
+    transitivePeerDependencies:
+      - '@types/react'
+      - immer
+
+  '@react-three/postprocessing@3.0.4(@react-three/fiber@9.5.0(@types/react@19.2.3)(expo-asset@12.0.12(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4)))(expo@54.0.33)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4)(three@0.183.2))(@types/three@0.183.1)(react@19.2.4)(three@0.183.2)':
+    dependencies:
+      '@react-three/fiber': 9.5.0(@types/react@19.2.3)(expo-asset@12.0.12(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4)))(expo@54.0.33)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react-native@0.83.1(@babel/core@7.29.0)(@types/react@19.2.3)(react@19.2.4))(react@19.2.4)(three@0.183.2)
+      maath: 0.6.0(@types/three@0.183.1)(three@0.183.2)
+      n8ao: 1.10.1(postprocessing@6.38.3(three@0.183.2))(three@0.183.2)
+      postprocessing: 6.38.3(three@0.183.2)
+      react: 19.2.4
+      three: 0.183.2
+    transitivePeerDependencies:
+      - '@types/three'
+
   '@reduxjs/toolkit@2.11.2(react@19.2.4)':
     dependencies:
       '@standard-schema/spec': 1.1.0
@@ -18580,6 +18784,16 @@ snapshots:
       fflate: 0.8.2
       meshoptimizer: 0.22.0
 
+  '@types/three@0.183.1':
+    dependencies:
+      '@dimforge/rapier3d-compat': 0.12.0
+      '@tweenjs/tween.js': 23.1.3
+      '@types/stats.js': 0.17.4
+      '@types/webxr': 0.5.24
+      '@webgpu/types': 0.1.69
+      fflate: 0.8.2
+      meshoptimizer: 1.0.1
+
   '@types/trusted-types@2.0.7': {}
 
   '@types/unist@2.0.11': {}
@@ -19705,6 +19919,10 @@ snapshots:
     dependencies:
       three: 0.182.0
 
+  camera-controls@3.1.2(three@0.183.2):
+    dependencies:
+      three: 0.183.2
+
   caniuse-lite@1.0.30001764: {}
 
   ccount@2.0.1: {}
@@ -23102,6 +23320,16 @@ snapshots:
       '@types/three': 0.182.0
       three: 0.182.0
 
+  maath@0.10.8(@types/three@0.183.1)(three@0.183.2):
+    dependencies:
+      '@types/three': 0.183.1
+      three: 0.183.2
+
+  maath@0.6.0(@types/three@0.183.1)(three@0.183.2):
+    dependencies:
+      '@types/three': 0.183.1
+      three: 0.183.2
+
   magic-string@0.30.21:
     dependencies:
       '@jridgewell/sourcemap-codec': 1.5.5
@@ -23322,8 +23550,14 @@ snapshots:
     dependencies:
       three: 0.182.0
 
+  meshline@3.3.1(three@0.183.2):
+    dependencies:
+      three: 0.183.2
+
   meshoptimizer@0.22.0: {}
 
+  meshoptimizer@1.0.1: {}
+
   metro-babel-transformer@0.83.3:
     dependencies:
       '@babel/core': 7.29.0
@@ -23898,6 +24132,11 @@ snapshots:
       object-assign: 4.1.1
       thenify-all: 1.6.0
 
+  n8ao@1.10.1(postprocessing@6.38.3(three@0.183.2))(three@0.183.2):
+    dependencies:
+      postprocessing: 6.38.3(three@0.183.2)
+      three: 0.183.2
+
   nano-spawn@2.0.0: {}
 
   nanoid@3.3.11: {}
@@ -24396,6 +24635,10 @@ snapshots:
 
   postgres@3.4.8: {}
 
+  postprocessing@6.38.3(three@0.183.2):
+    dependencies:
+      three: 0.183.2
+
   potpack@1.0.2: {}
 
   powershell-utils@0.1.0: {}
@@ -25860,6 +26103,11 @@ snapshots:
       '@types/three': 0.182.0
       three: 0.182.0
 
+  stats-gl@2.4.2(@types/three@0.183.1)(three@0.183.2):
+    dependencies:
+      '@types/three': 0.183.1
+      three: 0.183.2
+
   stats.js@0.17.0: {}
 
   statuses@1.5.0: {}
@@ -26237,16 +26485,6 @@ snapshots:
     optionalDependencies:
       esbuild: 0.25.0
 
-  terser-webpack-plugin@5.3.16(webpack@5.96.1):
-    dependencies:
-      '@jridgewell/trace-mapping': 0.3.31
-      jest-worker: 27.5.1
-      schema-utils: 4.3.3
-      serialize-javascript: 6.0.2
-      terser: 5.46.0
-      webpack: 5.96.1
-    optional: true
-
   terser@5.46.0:
     dependencies:
       '@jridgewell/source-map': 0.3.11
@@ -26274,6 +26512,10 @@ snapshots:
     dependencies:
       three: 0.182.0
 
+  three-mesh-bvh@0.8.3(three@0.183.2):
+    dependencies:
+      three: 0.183.2
+
   three-stdlib@2.36.1(three@0.182.0):
     dependencies:
       '@types/draco3d': 1.4.10
@@ -26284,8 +26526,20 @@ snapshots:
       potpack: 1.0.2
       three: 0.182.0
 
+  three-stdlib@2.36.1(three@0.183.2):
+    dependencies:
+      '@types/draco3d': 1.4.10
+      '@types/offscreencanvas': 2019.7.3
+      '@types/webxr': 0.5.24
+      draco3d: 1.5.7
+      fflate: 0.6.10
+      potpack: 1.0.2
+      three: 0.183.2
+
   three@0.182.0: {}
 
+  three@0.183.2: {}
+
   throat@5.0.0: {}
 
   throat@6.0.2: {}
@@ -26366,10 +26620,22 @@ snapshots:
       troika-worker-utils: 0.52.0
       webgl-sdf-generator: 1.1.1
 
+  troika-three-text@0.52.4(three@0.183.2):
+    dependencies:
+      bidi-js: 1.0.3
+      three: 0.183.2
+      troika-three-utils: 0.52.4(three@0.183.2)
+      troika-worker-utils: 0.52.0
+      webgl-sdf-generator: 1.1.1
+
   troika-three-utils@0.52.4(three@0.182.0):
     dependencies:
       three: 0.182.0
 
+  troika-three-utils@0.52.4(three@0.183.2):
+    dependencies:
+      three: 0.183.2
+
   troika-worker-utils@0.52.0: {}
 
   trough@2.2.0: {}
@@ -27046,37 +27312,6 @@ snapshots:
 
   webpack-sources@3.3.3: {}
 
-  webpack@5.96.1:
-    dependencies:
-      '@types/eslint-scope': 3.7.7
-      '@types/estree': 1.0.8
-      '@webassemblyjs/ast': 1.14.1
-      '@webassemblyjs/wasm-edit': 1.14.1
-      '@webassemblyjs/wasm-parser': 1.14.1
-      acorn: 8.15.0
-      browserslist: 4.28.1
-      chrome-trace-event: 1.0.4
-      enhanced-resolve: 5.19.0
-      es-module-lexer: 1.7.0
-      eslint-scope: 5.1.1
-      events: 3.3.0
-      glob-to-regexp: 0.4.1
-      graceful-fs: 4.2.11
-      json-parse-even-better-errors: 2.3.1
-      loader-runner: 4.3.1
-      mime-types: 2.1.35
-      neo-async: 2.6.2
-      schema-utils: 3.3.0
-      tapable: 2.3.0
-      terser-webpack-plugin: 5.3.16(webpack@5.96.1)
-      watchpack: 2.5.1
-      webpack-sources: 3.3.3
-    transitivePeerDependencies:
-      - '@swc/core'
-      - esbuild
-      - uglify-js
-    optional: true
-
   webpack@5.96.1(esbuild@0.25.0):
     dependencies:
       '@types/eslint-scope': 3.7.7

+ 180 - 0
skills/react-three-fiber/SKILL.md

@@ -0,0 +1,180 @@
+---
+name: react-three-fiber
+description: React Three Fiber 3D renderer for json-render. Use when working with @json-render/react-three-fiber, building 3D scenes from JSON specs, rendering meshes/lights/models/environments, or integrating Three.js with json-render catalogs.
+---
+
+# @json-render/react-three-fiber
+
+React Three Fiber renderer for json-render. 19 built-in 3D components.
+
+## Two Entry Points
+
+| Entry Point | Exports | Use For |
+|-------------|---------|---------|
+| `@json-render/react-three-fiber/catalog` | `threeComponentDefinitions` | Catalog schemas (no R3F dependency, safe for server) |
+| `@json-render/react-three-fiber` | `threeComponents`, `ThreeRenderer`, `ThreeCanvas`, schemas | R3F implementations and renderer |
+
+## Usage Pattern
+
+Pick the 3D components you need from the standard definitions:
+
+```typescript
+import { defineCatalog } from "@json-render/core";
+import { schema } from "@json-render/react/schema";
+import { threeComponentDefinitions } from "@json-render/react-three-fiber/catalog";
+import { defineRegistry } from "@json-render/react";
+import { threeComponents, ThreeCanvas } from "@json-render/react-three-fiber";
+
+// Catalog: pick definitions
+const catalog = defineCatalog(schema, {
+  components: {
+    Box: threeComponentDefinitions.Box,
+    Sphere: threeComponentDefinitions.Sphere,
+    AmbientLight: threeComponentDefinitions.AmbientLight,
+    DirectionalLight: threeComponentDefinitions.DirectionalLight,
+    OrbitControls: threeComponentDefinitions.OrbitControls,
+  },
+  actions: {},
+});
+
+// Registry: pick matching implementations
+const { registry } = defineRegistry(catalog, {
+  components: {
+    Box: threeComponents.Box,
+    Sphere: threeComponents.Sphere,
+    AmbientLight: threeComponents.AmbientLight,
+    DirectionalLight: threeComponents.DirectionalLight,
+    OrbitControls: threeComponents.OrbitControls,
+  },
+});
+```
+
+## Rendering
+
+### ThreeCanvas (convenience wrapper)
+
+```tsx
+<ThreeCanvas
+  spec={spec}
+  registry={registry}
+  shadows
+  camera={{ position: [5, 5, 5], fov: 50 }}
+  style={{ width: "100%", height: "100vh" }}
+/>
+```
+
+### Manual Canvas setup
+
+```tsx
+import { Canvas } from "@react-three/fiber";
+import { ThreeRenderer } from "@json-render/react-three-fiber";
+
+<Canvas shadows>
+  <ThreeRenderer spec={spec} registry={registry}>
+    {/* Additional R3F elements */}
+  </ThreeRenderer>
+</Canvas>
+```
+
+## Available Components (19)
+
+### Primitives (7)
+- `Box` -- width, height, depth, material
+- `Sphere` -- radius, widthSegments, heightSegments, material
+- `Cylinder` -- radiusTop, radiusBottom, height, material
+- `Cone` -- radius, height, material
+- `Torus` -- radius, tube, material
+- `Plane` -- width, height, material
+- `Capsule` -- radius, length, material
+
+All primitives share: `position`, `rotation`, `scale`, `castShadow`, `receiveShadow`, `material`.
+
+### Lights (4)
+- `AmbientLight` -- color, intensity
+- `DirectionalLight` -- position, color, intensity, castShadow
+- `PointLight` -- position, color, intensity, distance, decay
+- `SpotLight` -- position, color, intensity, angle, penumbra
+
+### Other (8)
+- `Group` -- container with position/rotation/scale, supports children
+- `Model` -- GLTF/GLB loader via url prop
+- `Environment` -- HDRI environment map (preset, background, blur, intensity)
+- `Fog` -- linear fog (color, near, far)
+- `GridHelper` -- reference grid (size, divisions, color)
+- `Text3D` -- SDF text (text, fontSize, color, anchorX, anchorY)
+- `PerspectiveCamera` -- camera (position, fov, near, far, makeDefault)
+- `OrbitControls` -- orbit controls (enableDamping, enableZoom, autoRotate)
+
+## Shared Schemas
+
+Reusable Zod schemas for custom 3D catalog definitions:
+
+```typescript
+import { vector3Schema, materialSchema, transformProps, shadowProps } from "@json-render/react-three-fiber";
+import { z } from "zod";
+
+// Custom 3D component
+const myComponentDef = {
+  props: z.object({
+    ...transformProps,
+    ...shadowProps,
+    material: materialSchema.nullable(),
+    myCustomProp: z.string(),
+  }),
+  description: "My custom 3D component",
+};
+```
+
+## Material Schema
+
+```typescript
+materialSchema = z.object({
+  color: z.string().nullable(),         // default "#ffffff"
+  metalness: z.number().nullable(),     // default 0
+  roughness: z.number().nullable(),     // default 1
+  emissive: z.string().nullable(),      // default "#000000"
+  emissiveIntensity: z.number().nullable(), // default 1
+  opacity: z.number().nullable(),       // default 1
+  transparent: z.boolean().nullable(),  // default false
+  wireframe: z.boolean().nullable(),    // default false
+});
+```
+
+## Spec Format
+
+3D specs use the standard json-render flat element format:
+
+```json
+{
+  "root": "scene",
+  "elements": {
+    "scene": {
+      "type": "Group",
+      "props": { "position": [0, 0, 0] },
+      "children": ["light", "box"]
+    },
+    "light": {
+      "type": "AmbientLight",
+      "props": { "intensity": 0.5 },
+      "children": []
+    },
+    "box": {
+      "type": "Box",
+      "props": {
+        "position": [0, 0.5, 0],
+        "material": { "color": "#4488ff", "metalness": 0.3, "roughness": 0.7 }
+      },
+      "children": []
+    }
+  }
+}
+```
+
+## Dependencies
+
+Peer dependencies required:
+- `@react-three/fiber` >= 8.0.0
+- `@react-three/drei` >= 9.0.0
+- `three` >= 0.160.0
+- `react` ^19.0.0
+- `zod` ^4.0.0