arkaos 5.5.0 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/THE-ARKAOS-GUIDE.md +1 -1
- package/VERSION +1 -1
- package/arka/SKILL.md +1 -1
- package/config/skills-provenance.yaml +9 -0
- package/departments/dev/skills/canvas-generative/SKILL.md +3 -3
- package/departments/dev/skills/threejs/SKILL.md +173 -0
- package/departments/dev/skills/threejs/references/genjutsu.LICENSE +21 -0
- package/departments/dev/skills/threejs/references/r3f.md +249 -0
- package/departments/dev/skills/threejs/references/scene-setup.md +404 -0
- package/departments/dev/skills/threejs/references/shaders.md +452 -0
- package/harness/codex/AGENTS.md +1 -1
- package/harness/copilot/copilot-instructions.md +1 -1
- package/harness/cursor/rules/arkaos.mdc +2 -2
- package/harness/gemini/GEMINI.md +1 -1
- package/harness/opencode/AGENTS.md +1 -1
- package/harness/opencode/agents/arka-architect-gabriel.md +1 -1
- package/harness/opencode/agents/arka-brand-director-valentina.md +1 -1
- package/harness/opencode/agents/arka-cfo-helena.md +1 -1
- package/harness/opencode/agents/arka-chief-of-staff-afonso.md +1 -1
- package/harness/opencode/agents/arka-community-strategist-beatriz.md +1 -1
- package/harness/opencode/agents/arka-content-strategist-rafael.md +1 -1
- package/harness/opencode/agents/arka-conversion-strategist-ines.md +1 -1
- package/harness/opencode/agents/arka-coo-sofia.md +1 -1
- package/harness/opencode/agents/arka-copy-director-eduardo.md +1 -1
- package/harness/opencode/agents/arka-cqo-marta.md +1 -1
- package/harness/opencode/agents/arka-cto-marco.md +1 -1
- package/harness/opencode/agents/arka-design-ops-lead-iris.md +1 -1
- package/harness/opencode/agents/arka-ecom-director-ricardo.md +1 -1
- package/harness/opencode/agents/arka-knowledge-director-clara.md +1 -1
- package/harness/opencode/agents/arka-leadership-director-rodrigo.md +1 -1
- package/harness/opencode/agents/arka-marketing-director-luna.md +1 -1
- package/harness/opencode/agents/arka-ops-lead-daniel.md +1 -1
- package/harness/opencode/agents/arka-pm-director-carolina.md +1 -1
- package/harness/opencode/agents/arka-revops-lead-vicente.md +1 -1
- package/harness/opencode/agents/arka-saas-strategist-tiago.md +1 -1
- package/harness/opencode/agents/arka-sales-director-miguel.md +1 -1
- package/harness/opencode/agents/arka-strategy-director-tomas.md +1 -1
- package/harness/opencode/agents/arka-tech-director-francisca.md +1 -1
- package/harness/opencode/agents/arka-tech-lead-paulo.md +1 -1
- package/harness/opencode/agents/arka-video-producer-simao.md +1 -1
- package/harness/zed/.rules +1 -1
- package/knowledge/commands-registry.json +1 -1
- package/knowledge/skills-manifest.json +16 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**The Operating System for AI Agent Teams.**
|
|
6
6
|
|
|
7
|
-
89 agents. 17 departments.
|
|
7
|
+
89 agents. 17 departments. 338 skills. Enterprise frameworks. Multi-runtime. One install.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npx arkaos install
|
|
@@ -97,7 +97,7 @@ npx arkaos doctor # Health check
|
|
|
97
97
|
|
|
98
98
|
### Skill packs, à la carte
|
|
99
99
|
|
|
100
|
-
The default install ships a curated core so your context window stays lean. Everything else lives in the ArkaOS plugin marketplace: 16 department packs with
|
|
100
|
+
The default install ships a curated core so your context window stays lean. Everything else lives in the ArkaOS plugin marketplace: 16 department packs with 264 skills, generated straight from the same sources the core uses. Inside Claude Code:
|
|
101
101
|
|
|
102
102
|
```
|
|
103
103
|
/plugin marketplace add andreagroferreira/arka-os
|
package/THE-ARKAOS-GUIDE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The ArkaOS Guide
|
|
2
2
|
|
|
3
|
-
> v5.
|
|
3
|
+
> v5.6.0 — 89 agents, 17 departments, 338 skills, 299 commands, 20 ADRs.
|
|
4
4
|
> One file, everything you need to start. Generated by `scripts/guide_gen.py` — never hand-edited.
|
|
5
5
|
|
|
6
6
|
## What it is
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.
|
|
1
|
+
5.6.0
|
package/arka/SKILL.md
CHANGED
|
@@ -63,7 +63,7 @@ complementary, not alternatives.
|
|
|
63
63
|
# ArkaOS — Main Orchestrator
|
|
64
64
|
|
|
65
65
|
> **The Operating System for AI Agent Teams**
|
|
66
|
-
> 89 agents. 17 departments.
|
|
66
|
+
> 89 agents. 17 departments. 338 skills. Multi-runtime. Dashboard. Knowledge RAG.
|
|
67
67
|
|
|
68
68
|
## ⛔ Evidence flow — 4 gates (NON-NEGOTIABLE)
|
|
69
69
|
|
|
@@ -139,6 +139,15 @@ derived:
|
|
|
139
139
|
origin: community
|
|
140
140
|
source: https://github.com/AThevon/genjutsu
|
|
141
141
|
license: MIT
|
|
142
|
+
# Animation/design absorption campaign PR-4 — dev/threejs. Dual-source:
|
|
143
|
+
# the SKILL.md core is ArkaOS-authored from the three.js project's own
|
|
144
|
+
# LLM guidance (docs/llms.txt, MIT — recorded in THIRD-PARTY-NOTICES);
|
|
145
|
+
# references/r3f.md, scene-setup.md and shaders.md derive from
|
|
146
|
+
# AThevon/genjutsu's threejs-r3f skill (MIT, primary source here).
|
|
147
|
+
departments/dev/skills/threejs:
|
|
148
|
+
origin: community
|
|
149
|
+
source: https://github.com/AThevon/genjutsu
|
|
150
|
+
license: MIT
|
|
142
151
|
# Marketing skills integration campaign — derived from
|
|
143
152
|
# coreyhaines31/marketingskills (MIT). Frontmatter metadata must match
|
|
144
153
|
# each entry field-for-field (test_registry_matches_frontmatter_field_for_field).
|
|
@@ -8,8 +8,8 @@ description: >
|
|
|
8
8
|
TRIGGER: "generative art", "arte generativa", "particles background",
|
|
9
9
|
"fundo de partículas", "noise field", "flow field", "canvas animation",
|
|
10
10
|
"fundo processual", "creative coding em canvas", "/dev canvas-generative".
|
|
11
|
-
SKIP: real-time 3D or shader work ->
|
|
12
|
-
|
|
11
|
+
SKIP: real-time 3D or shader work -> dev/threejs; a pre-rendered 3D
|
|
12
|
+
world scrubbed by scroll ->
|
|
13
13
|
dev/scroll-world; turning an existing video into a scroll site ->
|
|
14
14
|
dev/animated-website; charts and data graphics -> the dataviz skill.
|
|
15
15
|
allowed-tools: [Read, Write, Edit, Bash, Grep, Glob]
|
|
@@ -352,7 +352,7 @@ function update() {
|
|
|
352
352
|
|---|---|
|
|
353
353
|
| Noise implementations, particle pool, flow field, L-system, attractors | `references/algorithms.md` |
|
|
354
354
|
| Timing and easing for animated transitions | the brand guidelines' Motion System (§06), reached via the source-of-truth table in `departments/brand/references/uiux-knowledge-and-tools.md` |
|
|
355
|
-
| 3D generative (shaders, GPU particles) |
|
|
355
|
+
| 3D generative (shaders, GPU particles) | `departments/dev/skills/threejs/SKILL.md` |
|
|
356
356
|
|
|
357
357
|
## Output
|
|
358
358
|
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dev/threejs
|
|
3
|
+
description: >
|
|
4
|
+
Real-time 3D in the browser with three.js and React Three Fiber: modern
|
|
5
|
+
setup (import maps, WebGL vs WebGPU renderer choice, TSL), the
|
|
6
|
+
scene-camera-renderer wiring, geometries, materials and lights, loaders,
|
|
7
|
+
disposal discipline against GPU memory leaks, performance budgets — and
|
|
8
|
+
the R3F layer (hooks, drei, postprocessing) for React projects.
|
|
9
|
+
TRIGGER: "three.js", "threejs", "react three fiber", "r3f", "webgl",
|
|
10
|
+
"webgpu", "shader", "GLSL", "cena 3D", "3D interativo no browser",
|
|
11
|
+
"drei", "/dev threejs".
|
|
12
|
+
SKIP: a pre-rendered 3D world scrubbed by scroll (no real-time engine)
|
|
13
|
+
-> dev/scroll-world; generative 2D canvas -> dev/canvas-generative;
|
|
14
|
+
generating a 3D asset (GLB mesh) from an image -> the higgsfield-generate
|
|
15
|
+
skill; React motion without 3D -> dev/framer-motion.
|
|
16
|
+
allowed-tools: [Read, Write, Edit, Bash, Grep, Glob]
|
|
17
|
+
metadata:
|
|
18
|
+
origin: community
|
|
19
|
+
source: https://github.com/AThevon/genjutsu
|
|
20
|
+
license: MIT
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
<!-- arka:kb-first-prefix begin -->
|
|
24
|
+
> **KB-first:** query `mcp__obsidian__search_notes` (and
|
|
25
|
+
> `mcp__graphify__query_graph` when configured) and cite `[[wikilinks]]`
|
|
26
|
+
> or graph nodes — or declare the gap — BEFORE any external research.
|
|
27
|
+
> Full doctrine: `arka/SKILL.md` (KB-First Research).
|
|
28
|
+
<!-- arka:kb-first-prefix end -->
|
|
29
|
+
|
|
30
|
+
# Three.js — `/dev threejs`
|
|
31
|
+
|
|
32
|
+
> **Agent:** Diana (Frontend Dev) | **Framework:** three.js (MIT) + React Three Fiber
|
|
33
|
+
> Core authored from the three.js project's own LLM guidance
|
|
34
|
+
> (`docs/llms.txt`, MIT); the R3F layer derives from genjutsu (MIT) — see
|
|
35
|
+
> `references/`.
|
|
36
|
+
|
|
37
|
+
## Setup — the modern shape
|
|
38
|
+
|
|
39
|
+
Import maps, never legacy CDN script tags. Pin one version for `three`
|
|
40
|
+
and `three/addons/` together — mixing versions across the two specifiers
|
|
41
|
+
is the classic silent breaker:
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<script type="importmap">
|
|
45
|
+
{ "imports": {
|
|
46
|
+
"three": "https://cdn.jsdelivr.net/npm/three@<version>/build/three.module.js",
|
|
47
|
+
"three/webgpu": "https://cdn.jsdelivr.net/npm/three@<version>/build/three.webgpu.js",
|
|
48
|
+
"three/tsl": "https://cdn.jsdelivr.net/npm/three@<version>/build/three.tsl.js",
|
|
49
|
+
"three/addons/": "https://cdn.jsdelivr.net/npm/three@<version>/examples/jsm/" } }
|
|
50
|
+
</script>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`WebGPURenderer` is not exported from `three` — it lives in the
|
|
54
|
+
`three/webgpu` entry, and it is async: `await renderer.init()` before the
|
|
55
|
+
first frame. The TSL node functions live in `three/tsl`, not in
|
|
56
|
+
`three/webgpu`. Omit either entry and the import fails at runtime.
|
|
57
|
+
|
|
58
|
+
In a bundled project, `npm i three` and import the same specifiers. Check
|
|
59
|
+
the installed version before writing code — the API moves, and the
|
|
60
|
+
project's lockfile outranks your memory of it.
|
|
61
|
+
|
|
62
|
+
**Renderer choice.** `WebGLRenderer` is the default and safe everywhere.
|
|
63
|
+
`WebGPURenderer` (with TSL node materials) is the forward path but needs
|
|
64
|
+
feature detection and a WebGL fallback; choose it only when the project
|
|
65
|
+
targets browsers you control or ships the fallback. Never mix GLSL
|
|
66
|
+
`ShaderMaterial` into a WebGPU pipeline — that renderer wants TSL
|
|
67
|
+
`NodeMaterial` classes.
|
|
68
|
+
|
|
69
|
+
## The irreducible core
|
|
70
|
+
|
|
71
|
+
Scene, camera, renderer, loop — everything else hangs off these:
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
const scene = new THREE.Scene();
|
|
75
|
+
const camera = new THREE.PerspectiveCamera(75, w / h, 0.1, 100);
|
|
76
|
+
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
|
77
|
+
renderer.setSize(w, h);
|
|
78
|
+
renderer.setPixelRatio(Math.min(devicePixelRatio, 2)); // cap DPR: 3x buys nothing visible and burns fill rate
|
|
79
|
+
renderer.setAnimationLoop(tick); // not a hand-rolled rAF: in XR it swaps to the session's loop; outside XR it is rAF with correct teardown via setAnimationLoop(null)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
On resize: update `camera.aspect`, call `camera.updateProjectionMatrix()`,
|
|
83
|
+
then `renderer.setSize`. Forgetting the middle step is why "resize
|
|
84
|
+
stretches everything".
|
|
85
|
+
|
|
86
|
+
**Animate with elapsed time, not the frame count.** A per-frame delta
|
|
87
|
+
keeps speed identical at 30 and 144 fps; per-frame increments do not.
|
|
88
|
+
Use `THREE.Timer` (`timer.update(); timer.getDelta()`) — `THREE.Clock`
|
|
89
|
+
is deprecated since r183 and warns at runtime.
|
|
90
|
+
|
|
91
|
+
## Disposal — GPU memory does not garbage-collect
|
|
92
|
+
|
|
93
|
+
Removing a mesh from the scene frees nothing on the GPU. Every geometry,
|
|
94
|
+
material, texture and render target you create, you `dispose()` when its
|
|
95
|
+
life ends — and a material's textures are disposed separately from the
|
|
96
|
+
material. For a whole subtree, collect first, dispose once: `traverse`
|
|
97
|
+
visits Groups and Lights that have no material, and shared resources must
|
|
98
|
+
not be disposed per-mesh:
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
function disposeSubtree(root) {
|
|
102
|
+
const geometries = new Set(), materials = new Set(), textures = new Set();
|
|
103
|
+
root.traverse((obj) => {
|
|
104
|
+
if (!obj.isMesh && !obj.isPoints && !obj.isLine && !obj.isSprite) return;
|
|
105
|
+
// Sprites share one engine-owned quad geometry app-wide: never dispose it
|
|
106
|
+
if (obj.geometry && !obj.isSprite) geometries.add(obj.geometry);
|
|
107
|
+
const mats = Array.isArray(obj.material) ? obj.material : [obj.material];
|
|
108
|
+
for (const m of mats) {
|
|
109
|
+
if (!m) continue;
|
|
110
|
+
materials.add(m);
|
|
111
|
+
for (const v of Object.values(m)) if (v?.isTexture) textures.add(v);
|
|
112
|
+
// ShaderMaterial textures live in uniforms, not on the material —
|
|
113
|
+
// and a uniform's value can be an ARRAY of textures (atlas shape)
|
|
114
|
+
if (m.uniforms) for (const u of Object.values(m.uniforms)) {
|
|
115
|
+
const vals = Array.isArray(u?.value) ? u.value : [u?.value];
|
|
116
|
+
for (const v of vals) if (v?.isTexture) textures.add(v);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
textures.forEach((t) => t.dispose());
|
|
121
|
+
materials.forEach((m) => m.dispose());
|
|
122
|
+
geometries.forEach((g) => g.dispose());
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
This covers the shapes meshes ordinarily carry — geometries, material
|
|
127
|
+
maps, scalar and array texture uniforms. When a resource is shared
|
|
128
|
+
with meshes OUTSIDE the subtree — a texture atlas, a cached material —
|
|
129
|
+
exclude it: ownership is yours to model, and dispose belongs to the owner.
|
|
130
|
+
|
|
131
|
+
A single-page app that mounts and unmounts scenes without this leaks
|
|
132
|
+
until the tab dies. This is the number-one three.js production bug.
|
|
133
|
+
|
|
134
|
+
## Performance budget
|
|
135
|
+
|
|
136
|
+
- **Draw calls first.** Hundreds of meshes sharing one geometry+material
|
|
137
|
+
belong in an `InstancedMesh`; thousands of distinct static meshes merge
|
|
138
|
+
via `BufferGeometryUtils.mergeGeometries`.
|
|
139
|
+
- Reuse geometries and materials across meshes; clone only when a
|
|
140
|
+
property must diverge.
|
|
141
|
+
- Textures: compressed formats (KTX2/Basis) for anything big, and never
|
|
142
|
+
larger than the screen area they cover; mipmaps come from the loader or
|
|
143
|
+
`generateMipmaps`, with no power-of-two constraint in WebGL 2.
|
|
144
|
+
- Lights are per-fragment cost: prefer one directional + ambient or an
|
|
145
|
+
environment map over a constellation of point lights. Bake what does
|
|
146
|
+
not move.
|
|
147
|
+
- Shadows are a render pass per caster: tight `shadow.camera` frustum,
|
|
148
|
+
modest map sizes, `castShadow` only where it is visible.
|
|
149
|
+
|
|
150
|
+
## Loading models
|
|
151
|
+
|
|
152
|
+
`GLTFLoader` (with `DRACOLoader` or `KTX2Loader` when the asset uses
|
|
153
|
+
them) from `three/addons/`. Load asynchronously, add a loading state,
|
|
154
|
+
and dispose the loader's intermediates. glTF is the format; converting
|
|
155
|
+
from anything else happens in the asset pipeline, not at runtime.
|
|
156
|
+
|
|
157
|
+
## React Three Fiber
|
|
158
|
+
|
|
159
|
+
For React projects, R3F replaces the imperative wiring: the component
|
|
160
|
+
tree IS the scene graph, `useFrame` is the loop, drei supplies the
|
|
161
|
+
controls/loaders/staging you would otherwise hand-write. The full layer —
|
|
162
|
+
hooks discipline, drei picks, postprocessing, and the Do-Nots (from
|
|
163
|
+
setState in useFrame to forgotten dispose) — lives
|
|
164
|
+
in [references/r3f.md](references/r3f.md), with canvas/scene wiring in
|
|
165
|
+
[references/scene-setup.md](references/scene-setup.md) and GLSL/TSL
|
|
166
|
+
patterns in [references/shaders.md](references/shaders.md).
|
|
167
|
+
|
|
168
|
+
## Output
|
|
169
|
+
|
|
170
|
+
A working scene module (or R3F component tree) with the render loop,
|
|
171
|
+
resize handling and disposal wired, a stated performance budget (draw
|
|
172
|
+
calls, texture memory, DPR cap), and the renderer choice justified in one
|
|
173
|
+
line. Anything WebGPU/TSL states its fallback.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Adrien Thevon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
> Derived from genjutsu's `threejs-r3f` skill (MIT — `genjutsu.LICENSE`
|
|
2
|
+
> in this directory). The React Three Fiber layer of `dev/threejs`.
|
|
3
|
+
|
|
4
|
+
# Three.js & React Three Fiber
|
|
5
|
+
|
|
6
|
+
> 3D on the web. Three.js is the engine, R3F is the React renderer.
|
|
7
|
+
> Concise rules here. Deep-dive in `references/`.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## When to Use What
|
|
12
|
+
|
|
13
|
+
| Need | Tool | Why |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| Full 3D scene (models, lights, physics) | R3F + drei | Declarative, React-friendly, ecosystem |
|
|
16
|
+
| Vanilla 3D (no React) | Three.js direct | Lighter, no React overhead |
|
|
17
|
+
| Simple 3D transforms on UI | CSS `transform3d` | GPU-composited, no WebGL context |
|
|
18
|
+
| 2D particles / generative | Canvas 2D | Simpler API, less GPU overhead |
|
|
19
|
+
| Shader-only visuals (no scene graph) | Raw WebGL / ShaderMaterial | Maximum control, minimal abstraction |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Scene Setup Patterns
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { Canvas } from '@react-three/fiber'
|
|
27
|
+
import { Environment, OrbitControls } from '@react-three/drei'
|
|
28
|
+
import { Suspense } from 'react'
|
|
29
|
+
|
|
30
|
+
<Canvas camera={{ position: [0, 2, 5], fov: 45 }} dpr={[1, 2]} gl={{ antialias: true }}>
|
|
31
|
+
<Suspense fallback={null}>
|
|
32
|
+
<Environment preset="studio" />
|
|
33
|
+
<OrbitControls makeDefault />
|
|
34
|
+
<Scene />
|
|
35
|
+
</Suspense>
|
|
36
|
+
</Canvas>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Rules:**
|
|
40
|
+
- Always wrap scene content in `<Suspense>` -- loaders (GLTF, textures, HDRI) need it
|
|
41
|
+
- Set `dpr={[1, 2]}` to clamp pixel ratio (Retina without melting GPUs)
|
|
42
|
+
- Keep the Canvas parent component minimal -- re-renders propagate into the scene
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## R3F Hooks
|
|
47
|
+
|
|
48
|
+
| Hook | Purpose | Gotcha |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `useFrame((state, delta) => {})` | Per-frame logic (animation, physics) | Never setState inside |
|
|
51
|
+
| `useThree()` | Access gl, scene, camera, size, viewport, pointer | Destructure only what you need |
|
|
52
|
+
| `useLoader(TextureLoader, url)` | Load any Three.js resource | Wrap parent in Suspense |
|
|
53
|
+
| `useGraph(scene)` | Extract nodes/materials from loaded scene | Useful after useGLTF |
|
|
54
|
+
|
|
55
|
+
### useFrame Tips
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
useFrame((state, delta) => {
|
|
59
|
+
// Use delta for framerate-independent animation
|
|
60
|
+
meshRef.current.rotation.y += delta * 0.5
|
|
61
|
+
// Access clock for time-based effects
|
|
62
|
+
material.uniforms.uTime.value = state.clock.elapsedTime
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Drei Essentials
|
|
69
|
+
|
|
70
|
+
| Component | Use Case |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `Environment` | HDRI lighting (presets: studio, sunset, city, forest, dawn) |
|
|
73
|
+
| `Float` | Idle floating animation (speed, rotationIntensity, floatIntensity) |
|
|
74
|
+
| `Text3D` | Extruded 3D text (needs JSON font from Facetype.js) |
|
|
75
|
+
| `useGLTF` | Load .glb/.gltf models (returns { nodes, materials, scene }) |
|
|
76
|
+
| `useGLTF.preload(url)` | Preload model before component mounts |
|
|
77
|
+
| `MeshTransmissionMaterial` | Glass/crystal/liquid refraction effects |
|
|
78
|
+
| `PresentationControls` | Drag-to-rotate for product showcases |
|
|
79
|
+
| `Center` | Auto-center any group of meshes |
|
|
80
|
+
| `Detailed` | LOD -- swap geometry by camera distance |
|
|
81
|
+
| `useTexture` | Load textures with Suspense support |
|
|
82
|
+
| `Instances` | Declarative instancing for repeated meshes |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Postprocessing
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import { EffectComposer, Bloom, ChromaticAberration } from '@react-three/postprocessing'
|
|
90
|
+
import { BlendFunction } from 'postprocessing'
|
|
91
|
+
|
|
92
|
+
<EffectComposer>
|
|
93
|
+
<Bloom
|
|
94
|
+
luminanceThreshold={1}
|
|
95
|
+
luminanceSmoothing={0.4}
|
|
96
|
+
intensity={0.6}
|
|
97
|
+
/>
|
|
98
|
+
<ChromaticAberration
|
|
99
|
+
blendFunction={BlendFunction.NORMAL}
|
|
100
|
+
offset={[0.002, 0.002]}
|
|
101
|
+
/>
|
|
102
|
+
</EffectComposer>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Rules:**
|
|
106
|
+
- Bloom is selective by default -- lift material color/emissive above 1.0 to make it glow
|
|
107
|
+
- `luminanceThreshold={1}` = nothing glows unless explicitly emissive
|
|
108
|
+
- Order matters inside EffectComposer
|
|
109
|
+
- Effects are merged into a single pass (performant by design)
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Performance Patterns
|
|
114
|
+
|
|
115
|
+
| Pattern | When |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `<Instances>` / `InstancedMesh` | 100+ identical meshes (particles, trees, crowds) |
|
|
118
|
+
| `<Detailed distances={[0, 50, 100]}>` | LOD: swap hi/lo models by distance |
|
|
119
|
+
| `dispose={null}` on `<primitive>` | Prevent auto-dispose when reusing shared geometry |
|
|
120
|
+
| `useGLTF` + Draco | Compress .glb models (70-90% size reduction) |
|
|
121
|
+
| `useKTX2` (drei) | Compressed GPU textures (1/4 VRAM) — `useTexture` is TextureLoader-only and cannot decode .ktx2 |
|
|
122
|
+
| `frameloop="demand"` on Canvas | Only render when something changes (static scenes) |
|
|
123
|
+
| `invalidate()` from useThree | Trigger a render in demand mode |
|
|
124
|
+
| `<Canvas eventSource={ref}>` | Delegate pointer events to an ancestor element (overlay UIs); rendering stays on the main thread |
|
|
125
|
+
|
|
126
|
+
**Target metrics:** < 100 draw calls, < 1M triangles, 60fps on mid-range GPU.
|
|
127
|
+
Use `stats-gl` or `r3f-perf` to monitor.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Do Not
|
|
132
|
+
|
|
133
|
+
### 1. Never setState in useFrame
|
|
134
|
+
|
|
135
|
+
Causes full React re-render 60x/second. Mutate refs directly.
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
// BAD
|
|
139
|
+
useFrame(() => {
|
|
140
|
+
setRotation(prev => prev + 0.01) // React re-render every frame
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// GOOD
|
|
144
|
+
useFrame((_, delta) => {
|
|
145
|
+
meshRef.current.rotation.y += delta * 0.5 // Direct mutation, zero re-renders
|
|
146
|
+
})
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 2. Never allocate in the render loop
|
|
150
|
+
|
|
151
|
+
`new Vector3()` per frame = GC spikes = stutter.
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
// BAD
|
|
155
|
+
useFrame((state) => {
|
|
156
|
+
const target = new THREE.Vector3(0, Math.sin(state.clock.elapsedTime), 0)
|
|
157
|
+
meshRef.current.position.copy(target)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
// GOOD
|
|
161
|
+
const _target = useMemo(() => new THREE.Vector3(), [])
|
|
162
|
+
useFrame((state) => {
|
|
163
|
+
_target.set(0, Math.sin(state.clock.elapsedTime), 0)
|
|
164
|
+
meshRef.current.position.copy(_target)
|
|
165
|
+
})
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 3. Never forget dispose (memory leak)
|
|
169
|
+
|
|
170
|
+
Three.js textures, geometries, and materials live on the GPU. Unmounting a React component does NOT free them.
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
// BAD -- texture stays in VRAM after unmount
|
|
174
|
+
const texture = useLoader(TextureLoader, '/big-texture.jpg')
|
|
175
|
+
|
|
176
|
+
// GOOD -- R3F auto-disposes objects mounted as JSX primitives.
|
|
177
|
+
// useLoader results are CACHED globally: disposing the texture directly
|
|
178
|
+
// poisons the cache and the next consumer renders a disposed resource.
|
|
179
|
+
// Evict from the cache instead, then dispose:
|
|
180
|
+
useEffect(() => {
|
|
181
|
+
return () => {
|
|
182
|
+
useLoader.clear(TextureLoader, '/big-texture.jpg')
|
|
183
|
+
texture.dispose()
|
|
184
|
+
}
|
|
185
|
+
}, [texture])
|
|
186
|
+
|
|
187
|
+
// Resources YOU constructed (not via useLoader) are yours to dispose:
|
|
188
|
+
useEffect(() => () => { geometry.dispose(); material.dispose() }, [])
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### 4. Never re-render the Canvas parent
|
|
192
|
+
|
|
193
|
+
Parent state changes re-render the Canvas subtree — the Canvas does not
|
|
194
|
+
remount, but every child re-renders, props reconcile against the scene
|
|
195
|
+
graph, and R3F work you meant to keep out of React's render path runs
|
|
196
|
+
again. Keep fast-changing state out of the Canvas parent (zustand or
|
|
197
|
+
refs).
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
// BAD
|
|
201
|
+
function App() {
|
|
202
|
+
const [uiState, setUiState] = useState(false) // fast-changing state here re-renders the whole Canvas subtree
|
|
203
|
+
return (
|
|
204
|
+
<>
|
|
205
|
+
<button onClick={() => setUiState(!uiState)}>Toggle</button>
|
|
206
|
+
<Canvas><Scene config={uiState} /></Canvas>
|
|
207
|
+
</>
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// GOOD -- isolate Canvas in its own component
|
|
212
|
+
function App() {
|
|
213
|
+
return (
|
|
214
|
+
<>
|
|
215
|
+
<UI />
|
|
216
|
+
<SceneCanvas />
|
|
217
|
+
</>
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### 5. Never load assets without Suspense
|
|
223
|
+
|
|
224
|
+
Loaders (useGLTF, useTexture, useLoader) throw promises. Without Suspense, you get crashes.
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
// BAD
|
|
228
|
+
<Canvas>
|
|
229
|
+
<Model /> {/* useGLTF inside -- will throw */}
|
|
230
|
+
</Canvas>
|
|
231
|
+
|
|
232
|
+
// GOOD
|
|
233
|
+
<Canvas>
|
|
234
|
+
<Suspense fallback={<Loader />}>
|
|
235
|
+
<Model />
|
|
236
|
+
</Suspense>
|
|
237
|
+
</Canvas>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Quick Reference: Loading Sub-resources
|
|
243
|
+
|
|
244
|
+
| Need | Load |
|
|
245
|
+
|---|---|
|
|
246
|
+
| Scene boilerplate, lighting rigs, controls | `scene-setup.md` (this directory) |
|
|
247
|
+
| Custom shaders, GLSL patterns, uniforms | `shaders.md` (this directory) |
|
|
248
|
+
| Animation principles, easing, timing | the brand guidelines' Motion System (§06), reached via `departments/brand/references/uiux-knowledge-and-tools.md` |
|
|
249
|
+
| Scroll-scrubbed pre-rendered worlds (no live engine) | `departments/dev/skills/scroll-world/SKILL.md` |
|