arkaos 5.4.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 +27 -0
- package/departments/dev/skills/canvas-generative/SKILL.md +362 -0
- package/departments/dev/skills/canvas-generative/references/algorithms.md +934 -0
- package/departments/dev/skills/canvas-generative/references/genjutsu.LICENSE +21 -0
- package/departments/dev/skills/css-native/SKILL.md +353 -0
- package/departments/dev/skills/css-native/references/genjutsu.LICENSE +21 -0
- package/departments/dev/skills/css-native/references/modern-css.md +465 -0
- package/departments/dev/skills/framer-motion/SKILL.md +223 -0
- package/departments/dev/skills/framer-motion/references/api.md +312 -0
- package/departments/dev/skills/framer-motion/references/genjutsu.LICENSE +21 -0
- 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 +61 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/marketplace_gen.py +1 -1
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# Framer Motion — API Reference
|
|
2
|
+
|
|
3
|
+
> `motion` v11+ — `import { motion, AnimatePresence, ... } from "motion/react"`
|
|
4
|
+
|
|
5
|
+
## motion component — Props
|
|
6
|
+
|
|
7
|
+
### Animation
|
|
8
|
+
|
|
9
|
+
| Prop | Type | Description |
|
|
10
|
+
|------|------|-------------|
|
|
11
|
+
| `initial` | `Target \| VariantLabel \| false` | Initial state. `false` = no animation on mount (starts directly from `animate`) |
|
|
12
|
+
| `animate` | `Target \| VariantLabel` | Target state. Change = animates automatically |
|
|
13
|
+
| `exit` | `Target \| VariantLabel` | Exit animation (requires parent `AnimatePresence`) |
|
|
14
|
+
| `transition` | `Transition` | Transition config (duration, ease, type, delay...) |
|
|
15
|
+
| `variants` | `Variants` | Map of labels to targets. Propagate to motion children |
|
|
16
|
+
|
|
17
|
+
### Gesture props
|
|
18
|
+
|
|
19
|
+
| Prop | Type | Description |
|
|
20
|
+
|------|------|-------------|
|
|
21
|
+
| `whileHover` | `Target \| VariantLabel` | Animates during hover |
|
|
22
|
+
| `whileTap` | `Target \| VariantLabel` | Animates during press/tap |
|
|
23
|
+
| `whileFocus` | `Target \| VariantLabel` | Animates during focus |
|
|
24
|
+
| `whileDrag` | `Target \| VariantLabel` | Animates during drag |
|
|
25
|
+
| `whileInView` | `Target \| VariantLabel` | Animates when visible in the viewport |
|
|
26
|
+
|
|
27
|
+
### Drag
|
|
28
|
+
|
|
29
|
+
| Prop | Type | Description |
|
|
30
|
+
|------|------|-------------|
|
|
31
|
+
| `drag` | `boolean \| "x" \| "y"` | Enables drag. `true` = both axes |
|
|
32
|
+
| `dragConstraints` | `{ top, left, right, bottom } \| RefObject` | Drag limits. Ref = a parent element |
|
|
33
|
+
| `dragElastic` | `number \| { top, left, right, bottom }` | Elasticity outside constraints (0-1, default 0.35) |
|
|
34
|
+
| `dragMomentum` | `boolean` | Inertia after release (default true) |
|
|
35
|
+
| `dragSnapToOrigin` | `boolean` | Returns to initial position after release |
|
|
36
|
+
| `dragTransition` | `InertiaTransition` | Inertia transition config |
|
|
37
|
+
| `dragPropagation` | `boolean` | Propagates drag to draggable parents |
|
|
38
|
+
| `onDrag` | `(event, info: PanInfo) => void` | Callback during drag |
|
|
39
|
+
| `onDragStart` | `(event, info: PanInfo) => void` | Callback at drag start |
|
|
40
|
+
| `onDragEnd` | `(event, info: PanInfo) => void` | Callback at drag end |
|
|
41
|
+
| `onDirectionLock` | `(axis: "x" \| "y") => void` | Callback when direction is locked |
|
|
42
|
+
|
|
43
|
+
### Layout
|
|
44
|
+
|
|
45
|
+
| Prop | Type | Description |
|
|
46
|
+
|------|------|-------------|
|
|
47
|
+
| `layout` | `boolean \| "position" \| "size" \| "preserve-aspect"` | Automatically animates layout changes |
|
|
48
|
+
| `layoutId` | `string` | Shared layout — animates between components sharing the same layoutId |
|
|
49
|
+
| `layoutDependency` | `any` | Forces a layout recalculation when this value changes |
|
|
50
|
+
| `layoutScroll` | `boolean` | Compensates scroll offset in layout calculations |
|
|
51
|
+
| `onLayoutAnimationStart` | `() => void` | Callback at layout animation start |
|
|
52
|
+
| `onLayoutAnimationComplete` | `() => void` | Callback at layout animation end |
|
|
53
|
+
|
|
54
|
+
### Lifecycle callbacks
|
|
55
|
+
|
|
56
|
+
| Prop | Type | Description |
|
|
57
|
+
|------|------|-------------|
|
|
58
|
+
| `onAnimationStart` | `(definition) => void` | When an animation starts |
|
|
59
|
+
| `onAnimationComplete` | `(definition) => void` | When an animation ends |
|
|
60
|
+
| `onUpdate` | `(latest: ResolvedValues) => void` | On each frame during animation |
|
|
61
|
+
| `onViewportEnter` | `(entry) => void` | When the element enters the viewport |
|
|
62
|
+
| `onViewportLeave` | `(entry) => void` | When the element leaves the viewport |
|
|
63
|
+
|
|
64
|
+
### Style (MotionValue-compatible)
|
|
65
|
+
|
|
66
|
+
The `style` prop accepts `MotionValue` in addition to normal values:
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
const x = useMotionValue(0);
|
|
70
|
+
<motion.div style={{ x, opacity: 0.5 }} />
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Independent transforms** supported in `style`: `x`, `y`, `z`, `rotateX`, `rotateY`, `rotateZ`, `scale`, `scaleX`, `scaleY`, `skewX`, `skewY`.
|
|
74
|
+
|
|
75
|
+
## AnimatePresence — Props
|
|
76
|
+
|
|
77
|
+
| Prop | Type | Description |
|
|
78
|
+
|------|------|-------------|
|
|
79
|
+
| `mode` | `"sync" \| "wait" \| "popLayout"` | Enter/exit behavior. Default `"sync"` |
|
|
80
|
+
| `initial` | `boolean` | `false` = disables initial animations of children on first render |
|
|
81
|
+
| `onExitComplete` | `() => void` | Callback when all exit animations are finished |
|
|
82
|
+
| `custom` | `any` | Value passed to dynamic `exit` variants |
|
|
83
|
+
| `presenceAffectsLayout` | `boolean` | Does the exiting element still affect layout (default true) |
|
|
84
|
+
|
|
85
|
+
### Mode details
|
|
86
|
+
|
|
87
|
+
- **`sync`** (default) — enter and exit simultaneously
|
|
88
|
+
- **`wait`** — waits for exit to finish before starting enter (page transitions)
|
|
89
|
+
- **`popLayout`** — removes the element from flow immediately via `position: absolute`, starts enter during exit
|
|
90
|
+
|
|
91
|
+
## Transition — Configuration
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
// Spring (default for physical values)
|
|
95
|
+
transition={{ type: "spring", stiffness: 300, damping: 30, mass: 1 }}
|
|
96
|
+
|
|
97
|
+
// Tween (default for opacity, color)
|
|
98
|
+
transition={{ type: "tween", duration: 0.3, ease: "easeInOut" }}
|
|
99
|
+
|
|
100
|
+
// Inertia (for drag momentum)
|
|
101
|
+
transition={{ type: "inertia", velocity: 200, power: 0.8 }}
|
|
102
|
+
|
|
103
|
+
// Per-value
|
|
104
|
+
transition={{
|
|
105
|
+
x: { type: "spring", stiffness: 300 },
|
|
106
|
+
opacity: { duration: 0.2 },
|
|
107
|
+
}}
|
|
108
|
+
|
|
109
|
+
// Orchestration in variants
|
|
110
|
+
transition={{
|
|
111
|
+
staggerChildren: 0.08,
|
|
112
|
+
delayChildren: 0.2,
|
|
113
|
+
staggerDirection: -1, // reverse
|
|
114
|
+
when: "beforeChildren", // or "afterChildren"
|
|
115
|
+
}}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Ease presets
|
|
119
|
+
|
|
120
|
+
`"linear"`, `"easeIn"`, `"easeOut"`, `"easeInOut"`, `"circIn"`, `"circOut"`, `"circInOut"`, `"backIn"`, `"backOut"`, `"backInOut"`, `"anticipate"`
|
|
121
|
+
|
|
122
|
+
Cubic bezier: `ease: [0.25, 0.1, 0.25, 1.0]`
|
|
123
|
+
|
|
124
|
+
## Hooks
|
|
125
|
+
|
|
126
|
+
### useMotionValue
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
const x = useMotionValue(0);
|
|
130
|
+
// Set without re-render
|
|
131
|
+
x.set(100);
|
|
132
|
+
// Read current value
|
|
133
|
+
x.get(); // 100
|
|
134
|
+
// Bind to style
|
|
135
|
+
<motion.div style={{ x }} />
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Creates a reactive value that updates the DOM without React re-render. Foundation of all other hooks.
|
|
139
|
+
|
|
140
|
+
### useTransform
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
// Map a value to another
|
|
144
|
+
const opacity = useTransform(x, [-200, 0, 200], [0, 1, 0]);
|
|
145
|
+
|
|
146
|
+
// Transform with a function
|
|
147
|
+
const rounded = useTransform(x, (latest) => Math.round(latest));
|
|
148
|
+
|
|
149
|
+
// Combine multiple values
|
|
150
|
+
const combined = useTransform([x, y], ([latestX, latestY]) => latestX + latestY);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Derives a `MotionValue` from one or more others. Recalculated on each source change.
|
|
154
|
+
|
|
155
|
+
### useSpring
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
const springX = useSpring(motionValue, {
|
|
159
|
+
stiffness: 300, // default 100
|
|
160
|
+
damping: 30, // default 10
|
|
161
|
+
mass: 1, // default 1
|
|
162
|
+
restSpeed: 0.01,
|
|
163
|
+
restDelta: 0.01,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// With initial value (number)
|
|
167
|
+
const spring = useSpring(0);
|
|
168
|
+
spring.set(100); // animates to 100 with spring physics
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Creates a spring that follows a `MotionValue` or a number. Ideal for smooth scroll indicators, cursor followers.
|
|
172
|
+
|
|
173
|
+
### useScroll
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
// Page scroll
|
|
177
|
+
const { scrollX, scrollY, scrollXProgress, scrollYProgress } = useScroll();
|
|
178
|
+
|
|
179
|
+
// Target element scroll
|
|
180
|
+
const ref = useRef(null);
|
|
181
|
+
const { scrollYProgress } = useScroll({
|
|
182
|
+
target: ref,
|
|
183
|
+
offset: ["start end", "end start"],
|
|
184
|
+
// offset format: [targetEdge containerEdge]
|
|
185
|
+
// "start end" = when the top of target reaches the bottom of the container
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Specific container scroll
|
|
189
|
+
const { scrollYProgress } = useScroll({ container: scrollRef });
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Returns 4 `MotionValue`: absolute position (scrollX/Y) and normalized 0-1 progress (scrollXProgress/YProgress).
|
|
193
|
+
|
|
194
|
+
**Offset keywords**: `"start"`, `"center"`, `"end"`, or a number/percentage (`0.5`, `"100px"`).
|
|
195
|
+
|
|
196
|
+
### useAnimate
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
const [scope, animate] = useAnimate();
|
|
200
|
+
|
|
201
|
+
// Imperative animation
|
|
202
|
+
await animate(scope.current, { x: 100, opacity: 1 }, { duration: 0.5 });
|
|
203
|
+
|
|
204
|
+
// Selectors within the scope
|
|
205
|
+
await animate("li", { opacity: 1 }, { delay: stagger(0.1) });
|
|
206
|
+
|
|
207
|
+
// Sequence
|
|
208
|
+
await animate([
|
|
209
|
+
[scope.current, { x: 100 }],
|
|
210
|
+
["li", { opacity: 1 }, { delay: stagger(0.1) }],
|
|
211
|
+
]);
|
|
212
|
+
|
|
213
|
+
<div ref={scope}>
|
|
214
|
+
<li>...</li>
|
|
215
|
+
</div>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Imperative API for complex animations, sequences, and dynamic orchestrations. Alternative to variants when logic is too dynamic.
|
|
219
|
+
|
|
220
|
+
### useInView
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
const ref = useRef(null);
|
|
224
|
+
const isInView = useInView(ref, {
|
|
225
|
+
once: true, // triggers only once
|
|
226
|
+
amount: 0.5, // 50% visible (default "some")
|
|
227
|
+
margin: "-100px", // rootMargin (like IntersectionObserver)
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Usage with animate
|
|
231
|
+
<motion.div
|
|
232
|
+
ref={ref}
|
|
233
|
+
initial={{ opacity: 0, y: 50 }}
|
|
234
|
+
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
|
235
|
+
/>
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Wrapper around `IntersectionObserver`. Returns a reactive boolean.
|
|
239
|
+
|
|
240
|
+
### useReducedMotion
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
const prefersReduced = useReducedMotion();
|
|
244
|
+
|
|
245
|
+
<motion.div
|
|
246
|
+
animate={prefersReduced ? { opacity: 1 } : { opacity: 1, y: 0, scale: 1 }}
|
|
247
|
+
transition={prefersReduced ? { duration: 0 } : { type: "spring" }}
|
|
248
|
+
/>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Respects `prefers-reduced-motion`. **Always use it** for non-essential animations. Returns `true` if the user has enabled the system preference.
|
|
252
|
+
|
|
253
|
+
### useMotionValueEvent
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
const x = useMotionValue(0);
|
|
257
|
+
|
|
258
|
+
useMotionValueEvent(x, "change", (latest) => {
|
|
259
|
+
console.log("x changed to", latest);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
useMotionValueEvent(x, "animationStart", () => { /* ... */ });
|
|
263
|
+
useMotionValueEvent(x, "animationComplete", () => { /* ... */ });
|
|
264
|
+
useMotionValueEvent(x, "animationCancel", () => { /* ... */ });
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Subscribes to a MotionValue without re-render. Auto-cleanup on unmount. Prefer over `onChange` (deprecated).
|
|
268
|
+
|
|
269
|
+
## Utilities
|
|
270
|
+
|
|
271
|
+
### stagger
|
|
272
|
+
|
|
273
|
+
```tsx
|
|
274
|
+
import { stagger } from "motion/react";
|
|
275
|
+
|
|
276
|
+
animate("li", { opacity: 1 }, { delay: stagger(0.1) });
|
|
277
|
+
animate("li", { opacity: 1 }, { delay: stagger(0.1, { startDelay: 0.2 }) });
|
|
278
|
+
animate("li", { opacity: 1 }, { delay: stagger(0.1, { from: "center" }) });
|
|
279
|
+
// from: number | "first" | "center" | "last"
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### animate (standalone)
|
|
283
|
+
|
|
284
|
+
```tsx
|
|
285
|
+
import { animate } from "motion/react";
|
|
286
|
+
|
|
287
|
+
// Animate a value
|
|
288
|
+
animate(0, 100, {
|
|
289
|
+
duration: 1,
|
|
290
|
+
onUpdate: (latest) => el.style.opacity = latest,
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// Animate an element directly (outside React)
|
|
294
|
+
animate(element, { opacity: 1 }, { duration: 0.5 });
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### scroll (standalone)
|
|
298
|
+
|
|
299
|
+
```tsx
|
|
300
|
+
import { scroll } from "motion";
|
|
301
|
+
|
|
302
|
+
// Callback based on scroll progress
|
|
303
|
+
scroll((progress) => {
|
|
304
|
+
el.style.opacity = progress;
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Bind to an element's scroll
|
|
308
|
+
scroll(
|
|
309
|
+
(progress) => { /* ... */ },
|
|
310
|
+
{ target: element, offset: ["start end", "end start"] }
|
|
311
|
+
);
|
|
312
|
+
```
|
|
@@ -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,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.
|