Compare commits

..
Author SHA1 Message Date
Chris Tate cf3e229d99 add solid example 2026-03-11 22:44:16 -05:00
115 changed files with 114 additions and 10141 deletions
+1 -2
View File
@@ -20,8 +20,7 @@
"@json-render/image",
"@json-render/mcp",
"@json-render/svelte",
"@json-render/solid",
"@json-render/react-three-fiber"
"@json-render/solid"
]
],
"linked": [],
-44
View File
@@ -23,8 +23,6 @@ npm install @json-render/core @json-render/vue
npm install @json-render/core @json-render/svelte
# or for SolidJS
npm install @json-render/core @json-render/solid
# or for 3D scenes
npm install @json-render/core @json-render/react-three-fiber @react-three/fiber @react-three/drei three
```
## Why json-render?
@@ -123,7 +121,6 @@ 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 |
@@ -437,47 +434,6 @@ 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)
-9
View File
@@ -1,14 +1,5 @@
# web
## 0.1.7
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/codegen@0.13.0
- @json-render/react@0.13.0
## 0.1.6
### Patch Changes
@@ -1,416 +0,0 @@
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
View File
@@ -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-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.
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.
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.
+1 -1
View File
@@ -100,7 +100,7 @@ export function Header() {
>
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" />
</svg>
<span>12k</span>
<span>11k</span>
</a>
<ThemeToggle />
</nav>
-4
View File
@@ -134,10 +134,6 @@ 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
View File
@@ -56,7 +56,6 @@ 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",
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "web",
"version": "0.1.7",
"version": "0.1.6",
"type": "module",
"private": true,
"license": "Apache-2.0",
-9
View File
@@ -1,14 +1,5 @@
# example-chat
## 0.1.7
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/react@0.13.0
- @json-render/shadcn@0.13.0
## 0.1.6
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-chat",
"version": "0.1.7",
"version": "0.1.6",
"type": "module",
"private": true,
"scripts": {
-9
View File
@@ -1,14 +1,5 @@
# example-dashboard
## 0.1.7
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/codegen@0.13.0
- @json-render/react@0.13.0
## 0.1.6
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-dashboard",
"version": "0.1.7",
"version": "0.1.6",
"type": "module",
"private": true,
"scripts": {
-8
View File
@@ -1,13 +1,5 @@
# example-image
## 0.1.4
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/image@0.13.0
## 0.1.3
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-image",
"version": "0.1.4",
"version": "0.1.3",
"type": "module",
"private": true,
"scripts": {
-10
View File
@@ -1,15 +1,5 @@
# example-mcp
## 0.1.3
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/mcp@0.13.0
- @json-render/react@0.13.0
- @json-render/shadcn@0.13.0
## 0.1.2
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-mcp",
"version": "0.1.3",
"version": "0.1.2",
"type": "module",
"private": true,
"scripts": {
-9
View File
@@ -1,14 +1,5 @@
# example-no-ai
## 0.1.7
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/react@0.13.0
- @json-render/shadcn@0.13.0
## 0.1.6
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-no-ai",
"version": "0.1.7",
"version": "0.1.6",
"type": "module",
"private": true,
"scripts": {
-8
View File
@@ -1,13 +1,5 @@
# example-react-email
## 0.1.3
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/react-email@0.13.0
## 0.1.2
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-react-email",
"version": "0.1.3",
"version": "0.1.2",
"type": "module",
"private": true,
"scripts": {
-8
View File
@@ -1,13 +1,5 @@
# example-react-native
## 0.1.7
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/react-native@0.13.0
## 0.1.6
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-react-native",
"version": "0.1.7",
"version": "0.1.6",
"private": true,
"main": "expo-router/entry",
"scripts": {
-8
View File
@@ -1,13 +1,5 @@
# example-react-pdf
## 0.1.7
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/react-pdf@0.13.0
## 0.1.6
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-react-pdf",
"version": "0.1.7",
"version": "0.1.6",
"type": "module",
"private": true,
"scripts": {
-10
View File
@@ -1,10 +0,0 @@
# example-react-three-fiber
## 0.1.7
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/react-three-fiber@0.13.0
- @json-render/react@0.13.0
-20
View File
@@ -1,20 +0,0 @@
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
View File
@@ -1,238 +0,0 @@
"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>
);
}
@@ -1,9 +0,0 @@
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 }));
}
@@ -1,474 +0,0 @@
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: [],
},
},
},
};
@@ -1,692 +0,0 @@
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: [],
},
},
},
};
@@ -1,940 +0,0 @@
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: [],
},
},
},
};
@@ -1,164 +0,0 @@
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: [],
},
},
},
};
@@ -1,29 +0,0 @@
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,
];
@@ -1,400 +0,0 @@
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: [],
},
},
},
};
@@ -1,554 +0,0 @@
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: [],
},
},
},
};
@@ -1,307 +0,0 @@
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: [],
},
},
},
};
@@ -1,764 +0,0 @@
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: [],
},
},
},
};
@@ -1,809 +0,0 @@
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: [],
},
},
},
};
@@ -1,455 +0,0 @@
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: [],
},
},
},
};
@@ -1,149 +0,0 @@
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: [],
},
},
},
};
@@ -1,295 +0,0 @@
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: [],
},
},
},
};
@@ -1,12 +0,0 @@
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
View File
@@ -1,6 +0,0 @@
/// <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
View File
@@ -1,12 +0,0 @@
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
View File
@@ -1,35 +0,0 @@
{
"name": "example-react-three-fiber",
"version": "0.1.7",
"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
View File
@@ -1,13 +0,0 @@
{
"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"]
}
-8
View File
@@ -1,13 +1,5 @@
# example-remotion
## 0.1.7
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/remotion@0.13.0
## 0.1.6
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-remotion",
"version": "0.1.7",
"version": "0.1.6",
"type": "module",
"private": true,
"scripts": {
-9
View File
@@ -1,9 +0,0 @@
# example-solid
## 0.1.1
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/solid@0.13.0
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-solid",
"version": "0.1.1",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
@@ -1,13 +1,5 @@
# com.example.json-render-demo
## 0.0.8
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/react@0.13.0
## 0.0.7
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "com.example.json-render-demo",
"version": "0.0.8",
"version": "0.0.7",
"description": "Test",
"private": true,
"license": "~~proprietary~~",
@@ -1,13 +1,5 @@
# com.example.json-render-fullpage-demo
## 0.0.8
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/react@0.13.0
## 0.0.7
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "com.example.json-render-fullpage-demo",
"version": "0.0.8",
"version": "0.0.7",
"description": "Full-page Stripe App example (alpha)",
"private": true,
"license": "~~proprietary~~",
-8
View File
@@ -1,13 +1,5 @@
# svelte-chat
## 0.0.4
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/svelte@0.13.0
## 0.0.3
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "svelte-chat",
"private": true,
"version": "0.0.4",
"version": "0.0.3",
"type": "module",
"scripts": {
"dev": "vite dev",
-8
View File
@@ -1,13 +1,5 @@
# example-svelte
## 0.1.4
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/svelte@0.13.0
## 0.1.3
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-svelte",
"version": "0.1.4",
"version": "0.1.3",
"private": true,
"type": "module",
"scripts": {
-11
View File
@@ -1,16 +1,5 @@
# vite-renderers
## 0.1.5
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/solid@0.13.0
- @json-render/react@0.13.0
- @json-render/svelte@0.13.0
- @json-render/vue@0.13.0
## 0.1.4
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vite-renderers",
"version": "0.1.5",
"version": "0.1.4",
"private": true,
"type": "module",
"scripts": {
-8
View File
@@ -1,13 +1,5 @@
# example-vue
## 0.1.5
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/vue@0.13.0
## 0.1.4
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-vue",
"version": "0.1.5",
"version": "0.1.4",
"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)",
-7
View File
@@ -1,12 +1,5 @@
# @json-render/codegen
## 0.13.0
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
## 0.12.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@json-render/codegen",
"version": "0.13.0",
"version": "0.12.1",
"license": "Apache-2.0",
"description": "Utilities for generating code from json-render UI trees",
"keywords": [
-13
View File
@@ -1,18 +1,5 @@
# @json-render/core
## 0.13.0
### Minor Changes
- 5b32de8: Add SolidJS and React Three Fiber renderers, plus strict JSON Schema mode for LLM structured outputs.
### New:
- **`@json-render/solid`** -- SolidJS renderer. JSON becomes Solid components with reactive rendering, schema export, and full catalog support.
- **`@json-render/react-three-fiber`** -- React Three Fiber renderer. JSON becomes 3D scenes with 19 built-in components for meshes, lights, models, environments, text, cameras, and controls.
### Improved:
- **`@json-render/core`** -- `jsonSchema({ strict: true })` produces a JSON Schema subset compatible with LLM structured output APIs (OpenAI, Google Gemini, Anthropic). Ensures `additionalProperties: false` on every object and all properties listed in `required`.
## 0.12.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@json-render/core",
"version": "0.13.0",
"version": "0.12.1",
"license": "Apache-2.0",
"description": "JSON becomes real things. Define your catalog, register your components, let AI generate.",
"keywords": [
-1
View File
@@ -160,7 +160,6 @@ export { defineSchema } from "./schema";
// Catalog — defines the vocabulary (what components and actions are available)
export type {
Catalog,
JsonSchemaOptions,
PromptOptions,
PromptContext,
SpecValidationResult,
-188
View File
@@ -592,194 +592,6 @@ describe("catalog.jsonSchema", () => {
expect(jsonSchema).not.toBeNull();
expect(typeof jsonSchema).toBe("object");
});
describe("strict mode (LLM structured output compatible)", () => {
function hasNoPropertyNames(obj: unknown): boolean {
if (typeof obj !== "object" || obj === null) return true;
if ("propertyNames" in obj) return false;
return Object.values(obj).every(hasNoPropertyNames);
}
function allObjectsHaveAdditionalPropertiesFalse(obj: unknown): boolean {
if (typeof obj !== "object" || obj === null) return true;
const record = obj as Record<string, unknown>;
if (record.type === "object") {
if (record.additionalProperties !== false) return false;
}
return Object.values(record).every(
allObjectsHaveAdditionalPropertiesFalse,
);
}
function allObjectPropertiesRequired(obj: unknown): boolean {
if (typeof obj !== "object" || obj === null) return true;
const record = obj as Record<string, unknown>;
if (
record.type === "object" &&
record.properties &&
typeof record.properties === "object"
) {
const propKeys = Object.keys(record.properties);
const required = (record.required as string[]) ?? [];
if (!propKeys.every((k) => required.includes(k))) return false;
}
return Object.values(record).every(allObjectPropertiesRequired);
}
it("sets additionalProperties: false on all nested objects", () => {
const catalog = defineCatalog(testSchema, {
components: {
Card: {
props: z.object({
title: z.string(),
subtitle: z.string().optional(),
}),
description: "",
slots: [],
},
},
actions: {},
});
const schema = catalog.jsonSchema({ strict: true });
expect(allObjectsHaveAdditionalPropertiesFalse(schema)).toBe(true);
});
it("does not emit propertyNames", () => {
const catalog = defineCatalog(testSchema, {
components: {
Text: {
props: z.object({ content: z.string() }),
description: "",
slots: [],
},
},
actions: {},
});
const schema = catalog.jsonSchema({ strict: true });
expect(hasNoPropertyNames(schema)).toBe(true);
});
it("lists all properties in required (optional uses nullable)", () => {
const catalog = defineCatalog(testSchema, {
components: {
Card: {
props: z.object({
title: z.string(),
subtitle: z.string().optional(),
}),
description: "",
slots: [],
},
},
actions: {},
});
const schema = catalog.jsonSchema({ strict: true });
expect(allObjectPropertiesRequired(schema)).toBe(true);
});
it("converts record types without additionalProperties schema value", () => {
const catalog = defineCatalog(testSchema, {
components: {
Text: {
props: z.object({ content: z.string() }),
description: "",
slots: [],
},
},
actions: {},
});
const schema = catalog.jsonSchema({
strict: true,
}) as Record<string, unknown>;
// Walk the schema and ensure no additionalProperties is set to a non-false value
function noAdditionalPropertiesSchema(obj: unknown): boolean {
if (typeof obj !== "object" || obj === null) return true;
const rec = obj as Record<string, unknown>;
if (
"additionalProperties" in rec &&
rec.additionalProperties !== false
) {
return false;
}
return Object.values(rec).every(noAdditionalPropertiesSchema);
}
expect(noAdditionalPropertiesSchema(schema)).toBe(true);
});
it("wraps optional properties with anyOf nullable", () => {
// Use a schema where propsOf resolves to a single component's props
// (no record wrapper around the props) so the optional anyOf handling
// is directly visible in the JSON Schema output.
const flatSchema = defineSchema((s) => ({
spec: s.object({
component: s.object({
type: s.ref("catalog.components"),
props: s.propsOf("catalog.components"),
}),
}),
catalog: s.object({
components: s.map({
props: s.zod(),
description: s.string(),
}),
}),
}));
const catalog = defineCatalog(flatSchema, {
components: {
Card: {
props: z.object({
heading: z.string(),
caption: z.string().optional(),
}),
description: "",
},
},
});
const schema = catalog.jsonSchema({ strict: true }) as {
properties: {
component: {
properties: { props: Record<string, unknown> };
};
};
};
const propsSchema = schema.properties.component.properties.props;
// caption is optional – in strict mode it must be in `required`
// and wrapped in anyOf with null
const captionSchema = (
propsSchema as {
properties: { caption: Record<string, unknown> };
}
).properties.caption;
expect(captionSchema).toEqual({
anyOf: [{ type: "string" }, { type: "null" }],
});
const propsRequired = (propsSchema as { required: string[] }).required;
expect(propsRequired).toContain("heading");
expect(propsRequired).toContain("caption");
});
it("does not affect default (non-strict) output", () => {
const catalog = defineCatalog(testSchema, {
components: {
Text: {
props: z.object({ content: z.string() }),
description: "",
slots: [],
},
},
actions: {},
});
const defaultSchema = catalog.jsonSchema();
const defaultSchema2 = catalog.jsonSchema({ strict: false });
expect(defaultSchema).toEqual(defaultSchema2);
});
});
});
// =============================================================================
+33 -146
View File
@@ -92,7 +92,7 @@ export interface Catalog<
/** Generate system prompt for AI */
prompt(options?: PromptOptions): string;
/** Export as JSON Schema for structured outputs */
jsonSchema(options?: JsonSchemaOptions): object;
jsonSchema(): object;
/** Validate a spec against this catalog */
validate(spec: unknown): SpecValidationResult<InferSpec<TDef, TCatalog>>;
/** Get the Zod schema for the spec */
@@ -101,28 +101,6 @@ export interface Catalog<
readonly _specType: InferSpec<TDef, TCatalog>;
}
/**
* Options for JSON Schema export
*/
export interface JsonSchemaOptions {
/**
* When true, produces a strict JSON Schema subset compatible with
* LLM structured output APIs (OpenAI, Google Gemini, Anthropic, etc.).
* This ensures:
* - `additionalProperties: false` on every object
* - All object properties listed in `required` (optionals use nullable types)
* - Record/map types converted to fixed-key objects
*
* **Limitation:** Record types (dynamic-key maps) cannot be represented in
* strict JSON Schema because `additionalProperties` must be `false`. They
* are emitted as `{ type: "object", properties: {}, additionalProperties: false }`.
* The LLM prompt (via `catalog.prompt()`) still describes the full structure,
* so the model can produce valid output even though the JSON Schema for
* record entries is opaque.
*/
strict?: boolean;
}
/**
* Prompt generation options
*/
@@ -436,8 +414,8 @@ function createCatalogFromSchema<TDef extends SchemaDefinition, TCatalog>(
return generatePrompt(this, options);
},
jsonSchema(options: JsonSchemaOptions = {}): object {
return zodToJsonSchema(zodSchema, options.strict ?? false);
jsonSchema(): object {
return zodToJsonSchema(zodSchema);
},
validate(spec: unknown): SpecValidationResult<InferSpec<TDef, TCatalog>> {
@@ -1341,111 +1319,44 @@ function formatZodType(schema: z.ZodType): string {
}
/**
* Resolve the Zod type name from a schema's internal definition.
* Supports both Zod 3 (`_def.typeName`) and Zod 4 (`_def.type`).
* Convert Zod schema to JSON Schema
*/
function zodTypeName(def: Record<string, unknown>): string {
// Zod 4 uses _def.type as a plain string (e.g. "string", "object")
if (typeof def.type === "string") return def.type;
// Zod 3 uses _def.typeName (e.g. "ZodString", "ZodObject")
if (typeof def.typeName === "string") return def.typeName;
return "";
}
/**
* Normalise a Zod type name to a canonical lowercase form.
* Handles both Zod 3 ("ZodString") and Zod 4 ("string") conventions.
*/
function normalizeTypeName(raw: string): string {
// Zod 3 names start with "Zod", e.g. "ZodString" → "string"
if (raw.startsWith("Zod")) {
return raw.slice(3).toLowerCase();
}
return raw.toLowerCase();
}
/**
* Convert Zod schema to JSON Schema.
*
* When `strict` is true the output conforms to the JSON Schema subset required
* by LLM structured output APIs (no `propertyNames`, `additionalProperties: false`
* everywhere, all properties listed in `required`).
*/
function zodToJsonSchema(schema: z.ZodType, strict = false): object {
function zodToJsonSchema(schema: z.ZodType): object {
// Simplified JSON Schema conversion
const def = schema._def as unknown as Record<string, unknown>;
const kind = normalizeTypeName(zodTypeName(def));
const typeName = (def.typeName as string) ?? "";
switch (kind) {
case "string":
switch (typeName) {
case "ZodString":
return { type: "string" };
case "number":
case "ZodNumber":
return { type: "number" };
case "boolean":
case "ZodBoolean":
return { type: "boolean" };
case "literal": {
// Zod 4: _def.values (array), Zod 3: _def.value (single)
const values = def.values as unknown[] | undefined;
const value = values ? values[0] : def.value;
return { const: value };
}
case "enum": {
// Zod 4: _def.entries (object { a:"a", b:"b" }), Zod 3: _def.values (string[])
const entries = def.entries as Record<string, string> | undefined;
const values = entries
? Object.values(entries)
: (def.values as string[] | undefined);
return { enum: values ?? [] };
}
case "array": {
// Zod 4: _def.element, Zod 3: _def.type
const inner = (def.element ?? def.type) as z.ZodType | undefined;
case "ZodLiteral":
return { const: def.value };
case "ZodEnum":
return { enum: def.values };
case "ZodArray": {
const inner = def.type as z.ZodType | undefined;
return {
type: "array",
items: inner ? zodToJsonSchema(inner, strict) : {},
items: inner ? zodToJsonSchema(inner) : {},
};
}
case "object": {
// Zod 4: _def.shape is an object, Zod 3: _def.shape is a function
const rawShape = def.shape;
const shape: Record<string, z.ZodType> | undefined =
typeof rawShape === "function"
? (rawShape as () => Record<string, z.ZodType>)()
: (rawShape as Record<string, z.ZodType> | undefined);
if (!shape) {
if (strict) {
return {
type: "object",
properties: {},
required: [],
additionalProperties: false,
};
}
return { type: "object" };
}
case "ZodObject": {
const shape = (def.shape as () => Record<string, z.ZodType>)?.();
if (!shape) return { type: "object" };
const properties: Record<string, object> = {};
const required: string[] = [];
for (const [key, value] of Object.entries(shape)) {
properties[key] = zodToJsonSchema(value);
const innerDef = value._def as unknown as Record<string, unknown>;
const innerKind = normalizeTypeName(zodTypeName(innerDef));
const isOptional = innerKind === "optional" || innerKind === "nullable";
if (strict) {
// In strict mode, all properties must be in required.
// Optional properties are represented as nullable types.
if (
innerDef.typeName !== "ZodOptional" &&
innerDef.typeName !== "ZodNullable"
) {
required.push(key);
if (isOptional) {
const unwrapped = zodToJsonSchema(value, strict);
properties[key] = { anyOf: [unwrapped, { type: "null" }] };
} else {
properties[key] = zodToJsonSchema(value, strict);
}
} else {
properties[key] = zodToJsonSchema(value);
if (!isOptional) {
required.push(key);
}
}
}
return {
@@ -1455,47 +1366,23 @@ function zodToJsonSchema(schema: z.ZodType, strict = false): object {
additionalProperties: false,
};
}
case "record": {
case "ZodRecord": {
const valueType = def.valueType as z.ZodType | undefined;
if (strict) {
// LLM strict schemas require `additionalProperties: false` and do not
// permit a schema value for `additionalProperties`. Since record types
// have dynamic keys that cannot be enumerated at schema-generation time,
// we emit an opaque object. The LLM prompt still describes the expected
// structure so the model can produce valid output.
return {
type: "object",
properties: {},
required: [],
additionalProperties: false,
};
}
return {
type: "object",
additionalProperties: valueType ? zodToJsonSchema(valueType) : true,
};
}
case "optional":
case "nullable": {
case "ZodOptional":
case "ZodNullable": {
const inner = def.innerType as z.ZodType | undefined;
return inner ? zodToJsonSchema(inner, strict) : {};
return inner ? zodToJsonSchema(inner) : {};
}
case "union": {
case "ZodUnion": {
const options = def.options as z.ZodType[] | undefined;
return options
? { anyOf: options.map((o) => zodToJsonSchema(o, strict)) }
: {};
return options ? { anyOf: options.map(zodToJsonSchema) } : {};
}
case "any":
case "unknown":
if (strict) {
return {
type: "object",
properties: {},
required: [],
additionalProperties: false,
};
}
case "ZodAny":
return {};
default:
return {};
-7
View File
@@ -1,12 +1,5 @@
# @json-render/image
## 0.13.0
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
## 0.12.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@json-render/image",
"version": "0.13.0",
"version": "0.12.1",
"license": "Apache-2.0",
"description": "Image renderer for @json-render/core. JSON becomes SVG and PNG images via Satori.",
"keywords": [
-7
View File
@@ -1,12 +1,5 @@
# @json-render/jotai
## 0.13.0
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
## 0.12.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@json-render/jotai",
"version": "0.13.0",
"version": "0.12.1",
"license": "Apache-2.0",
"description": "Jotai adapter for json-render StateStore",
"keywords": [
-7
View File
@@ -1,12 +1,5 @@
# @json-render/mcp
## 0.13.0
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
## 0.12.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@json-render/mcp",
"version": "0.13.0",
"version": "0.12.1",
"license": "Apache-2.0",
"description": "MCP Apps integration for @json-render/core. Serve json-render UIs as interactive MCP Apps in Claude, ChatGPT, Cursor, and VS Code.",
"keywords": [
-7
View File
@@ -1,12 +1,5 @@
# @json-render/react-email
## 0.13.0
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
## 0.12.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@json-render/react-email",
"version": "0.13.0",
"version": "0.12.1",
"license": "Apache-2.0",
"description": "React Email renderer for @json-render/core. JSON becomes HTML emails.",
"keywords": [
-7
View File
@@ -1,12 +1,5 @@
# @json-render/react-native
## 0.13.0
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
## 0.12.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@json-render/react-native",
"version": "0.13.0",
"version": "0.12.1",
"license": "Apache-2.0",
"description": "React Native renderer for @json-render/core. JSON becomes React Native components.",
"keywords": [
-7
View File
@@ -1,12 +1,5 @@
# @json-render/react-pdf
## 0.13.0
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
## 0.12.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@json-render/react-pdf",
"version": "0.13.0",
"version": "0.12.1",
"license": "Apache-2.0",
"description": "React PDF renderer for @json-render/core. JSON becomes PDF documents.",
"keywords": [
-7
View File
@@ -1,12 +1,5 @@
# @internal/react-state
## 0.8.7
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
## 0.8.6
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@internal/react-state",
"version": "0.8.7",
"version": "0.8.6",
"private": true,
"license": "Apache-2.0",
"description": "Shared React state context for json-render renderer packages",
-20
View File
@@ -1,20 +0,0 @@
# @json-render/react-three-fiber
## 0.13.0
### Minor Changes
- 5b32de8: Add SolidJS and React Three Fiber renderers, plus strict JSON Schema mode for LLM structured outputs.
### New:
- **`@json-render/solid`** -- SolidJS renderer. JSON becomes Solid components with reactive rendering, schema export, and full catalog support.
- **`@json-render/react-three-fiber`** -- React Three Fiber renderer. JSON becomes 3D scenes with 19 built-in components for meshes, lights, models, environments, text, cameras, and controls.
### Improved:
- **`@json-render/core`** -- `jsonSchema({ strict: true })` produces a JSON Schema subset compatible with LLM structured output APIs (OpenAI, Google Gemini, Anthropic). Ensures `additionalProperties: false` on every object and all properties listed in `required`.
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
- @json-render/react@0.13.0
-75
View File
@@ -1,75 +0,0 @@
# @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/react-three-fiber](https://json-render.dev/docs/api/react-three-fiber)
-80
View File
@@ -1,80 +0,0 @@
{
"name": "@json-render/react-three-fiber",
"version": "0.13.0",
"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
View File
@@ -1,841 +0,0 @@
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"]>;
@@ -1,909 +0,0 @@
// @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
View File
@@ -1,25 +0,0 @@
// 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
View File
@@ -1,19 +0,0 @@
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
View File
@@ -1,144 +0,0 @@
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
View File
@@ -1,25 +0,0 @@
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
View File
@@ -1,9 +0,0 @@
{
"extends": "@internal/typescript-config/react-library.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
-19
View File
@@ -1,19 +0,0 @@
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",
],
});
-7
View File
@@ -1,12 +1,5 @@
# @json-render/react
## 0.13.0
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
## 0.12.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@json-render/react",
"version": "0.13.0",
"version": "0.12.1",
"license": "Apache-2.0",
"description": "React renderer for @json-render/core. JSON becomes React components.",
"keywords": [
-7
View File
@@ -1,12 +1,5 @@
# @json-render/redux
## 0.13.0
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
## 0.12.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@json-render/redux",
"version": "0.13.0",
"version": "0.12.1",
"license": "Apache-2.0",
"description": "Redux adapter for json-render StateStore",
"keywords": [
-7
View File
@@ -1,12 +1,5 @@
# @json-render/remotion
## 0.13.0
### Patch Changes
- Updated dependencies [5b32de8]
- @json-render/core@0.13.0
## 0.12.1
### Patch Changes

Some files were not shown because too many files have changed in this diff Show More