three-low-poly 0.9.24 → 0.9.26

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 CHANGED
@@ -1,121 +1,108 @@
1
1
  # Three.js Low Poly
2
2
 
3
- Create or enhance stylized low-poly scenes entirely through code procedurally generated geometries, prefabricated models, factory utilities, and atmospheric layers for [Three.js](https://threejs.org).
3
+ Create or enhance stylized low-poly scenes entirely through code using procedurally generated geometries, prefabricated models, factory utilities, and atmospheric layers for [Three.js](https://threejs.org).
4
4
 
5
- > **How to use this document** — Start here for intent, conventions, and direction. The [example gallery](https://jasonsturges.com/three-low-poly/) and TypeScript exports are the API reference; this file is the mental model. Written for humans first, structured so an AI collaborator can pick up full context from one read.
5
+ ![screen-capture](https://github.com/user-attachments/assets/3285ed9a-da3c-4287-ad2f-0c7e82cd70fd)
6
+ _Example Library scene_
6
7
 
7
- **Contents:** [What this is](#what-this-library-is) · [Mental model](#mental-model) · [Usage patterns](#usage-patterns) · [Conventions](#conventions-library-code) · [Current state](#current-state) · [Design patterns (target)](#design-patterns-target) · [Roadmap](#roadmap) · [Getting started](#getting-started)
8
+ ## Overview
8
9
 
9
- ---
10
+ This is an alternative to a modeling pipeline using external DCC tools: author in code via procedural and parametric geometry. Splines, lathe, extrude, parabolic profiles expressing geometry as algorithms. Vertex-built geometry, prefab models, and factory assemblies are first-class.
10
11
 
11
- ## What this library is
12
+ Most adopters will likely import their own assets (glTF, etc.) and use this library as scene enhancement rather than a full replacement for modeling software. This library spans both worlds.
12
13
 
13
- ### Origin
14
+ "Low poly" describes the aesthetic, not the whole thesis. Intention is code-first, parametric, performant scene building. Exported assets often mean one draw call per mesh; this library favors merged geometry, material groups, and instancing so dense procedural scenes stay batch-friendly, a deliberate contrast to typical glTF import cost.
14
15
 
15
- An alternative to the Blender → export → load pipeline. Model in code via **procedural** and **parametric** geometry instead of external DCC tools. Splines, lathe, extrude, parabolic profiles — geometry as algorithms, not hand-authored meshes.
16
+ ### Core principles
16
17
 
17
- ### Practical reality
18
+ - **Procedural generation**: geometry from algorithms, not imported meshes
19
+ - **Parametric modeling**: size, color, detail, and variation controlled via options
20
+ - **Prefabricated models**: drop-in objects (trees, jars, books) ready for `scene.add()`
21
+ - **Factory utilities**: assemblies and fills (rows, grids, tiled floors) from one call
22
+ - **Instanced layers**: weather, scatter, and particles via `InstancedMesh` / `Points`
23
+ - **Time-based animation**: driven by elapsed seconds, not per-frame magic numbers
18
24
 
19
- Most adopters will import their own assets (glTF, etc.) and use this library as **scene enhancement** — blowing leaves, rain, lightning, effervescence in a jar, procedural fill on a shelf — not as a full replacement for modeling software. That is expected and supported.
25
+ ## Modeling
20
26
 
21
- There is still real **modeling capability** here: vertex-built geometry, prefab models, and factory assemblies are first-class. The library spans both worlds.
27
+ Vertex modeling is preferred over relying on built-in primitives, using geometry groups when a mesh needs multiple materials. Profile-based tools (splines, lathe, extrude, parabolic curves) cover most organic and architectural shapes; NURBS surfaces are exploratory, for cases where profile tools fall short.
22
28
 
23
- ### Style
29
+ Where it makes sense, geometry is authored as its own class mirroring how Three.js structures its own `BufferGeometry` subclasses, rather than building shapes inline wherever they're used. That keeps geometry reusable and consistent with how the rest of the ecosystem expects to work with it.
24
30
 
25
- Generally low-poly and stylized, but “low poly” describes the aesthetic, not the whole thesis. The thesis is **code-first, parametric, performant scene building**.
31
+ ## Models, factories, and instancing
26
32
 
27
- ### Performance
33
+ Two distinct shapes of API exist, and the distinction is intentional:
28
34
 
29
- Exported assets often mean **one draw call per mesh**. This library favors **merged geometry, material groups, and instancing** so dense procedural scenes stay batch-friendly a deliberate contrast to typical glTF import cost.
35
+ - **Models ("prefabs")** are stable, ready-to-place objects - geometry plus materials combined into something you just add to a scene. Use these when the artifact itself is the thing you want.
36
+ - **Factories** build assemblies or fill an area - a row of books, a floor of hex tiles, a fence along a path. Use these when the artifact is a composition of many parts rather than a single object.
30
37
 
31
- ### Core principles
38
+ Dense or repeated elements (tiles, books, particles, weather) lean on `InstancedMesh` rather than individual meshes, which is where most of the performance comes from. Batching and merge opportunities are still handled case-by-case rather than enforced everywhere, so there's room to be deliberate about it when adding new factories.
32
39
 
33
- | Principle | Meaning |
34
- |-----------|---------|
35
- | **Procedural generation** | Geometry from algorithms, not imported meshes |
36
- | **Parametric modeling** | Size, color, detail, and variation via options interfaces |
37
- | **Prefabricated models** | Drop-in `Mesh` subclasses (trees, jars, books) ready for `scene.add()` |
38
- | **Factory utilities** | Assemblies and fills (book rows, tile floors, walls) from one call |
39
- | **Instanced layers** | Weather, scatter, and particles via `InstancedMesh` / `Points` |
40
- | **Time-based animation** | `update(dt)` in seconds, not per-frame magic numbers |
41
- | **Unique runtime, optional repeatability** | Stochastic variation by default; seeded reproducibility is roadmap (see below) |
42
-
43
- ![screen-capture](https://github.com/user-attachments/assets/3285ed9a-da3c-4287-ad2f-0c7e82cd70fd)
44
- _Example Library scene_
40
+ ## Atmosphere and effect layers
45
41
 
46
- ---
42
+ Anything that moves, flickers, or drifts falls into one of three categories, distinguished by how long it lasts, what it's scoped to, and what triggers it. This taxonomy matters more than it might first appear - it's the difference between something that's always running in the background versus something that fires once and finishes, and it should hold as the library grows:
47
43
 
48
- ## Mental model
44
+ - **Environment**: continuous, scene- or region-scale, generally always on (rain, fog, lightning, a starfield sky dome). These set the atmosphere for the whole scene rather than belonging to any one object.
45
+ - **Ambience**: continuous, but scoped to a volume or bound to a specific prop (bubbles in a jar, petals drifting through a yard, a flickering flame). Same always-on lifecycle as environment layers, just localized rather than global.
46
+ - **FX**: short-lived and event-triggered (dust kicked up on landing, a spell flash). Not just a smaller environment layer - the lifecycle is fundamentally different: it starts on `trigger()`, runs once, and ends, rather than running continuously via `update(dt)`.
49
47
 
50
- ### Package layers (where code lives)
48
+ ## Conventions
51
49
 
52
- | Layer | Role | Examples |
53
- |-------|------|----------|
54
- | **Geometry** | Building blocks; prefer when mirroring Three.js patterns | `JarGeometry`, `TestTubeGeometry`, `BrickGeometry` |
55
- | **Models (“prefabs”)** | Geometry + materials as ready-to-place objects | `Jar`, `Tree`, `TestTube`, `Mausoleum` |
56
- | **Factories** | Composite assemblies and **fills** along extent | `rowOfBooksByCount`, `createHexagonalTilesByRadius` |
57
- | **Environment** | Continuous scene- or region-scale atmosphere | `StarFieldEffect`, `RainEffect`, `LightningEffect`, `GroundFogEffect` |
58
- | **Ambience** | Continuous layers, often volume- or prop-bound | `PetalDriftEffect`, `EffervescenceEffect`, `WispEffect`, `EmissivePulseEffect`, `FlameFlickerEffect`, `GlowHalo` |
59
- | **FX** *(planned)* | Short-lived, event-triggered bursts | Dust on landing, spell flash, wind streak *(not in SDK yet)* |
60
- | **Animators** | Camera choreography and paths | `cameraOrbitAnimation`, `cameraFlythroughAnimation` |
61
- | **Utilities** | Math, easing, centering, RNG | `Easing`, `randomFloat`, `centerObject` |
50
+ A few patterns hold consistently across the library, and are worth following when contributing or extending it:
62
51
 
63
- **Model examples** showcase geometry. **Effect examples** showcase layers. **Scene examples** compose both (e.g. Mad Science: jar + effervescence).
52
+ - **Options objects**: public APIs take a `Options` interface (exported alongside the class) rather than positional arguments, with defaults set via destructuring and each field documented.
53
+ - **Animation**: anything that moves exposes `update(dt)` where `dt` is elapsed seconds, not a frame-rate-dependent step.
54
+ - **Disposal**: anything owning geometry, materials, or textures exposes `dispose()`.
55
+ - **Documentation**: every public class gets a doc comment with a minimal setup snippet, enough to wire it up without reading the source.
56
+ - **Examples**: every feature has a matching example in the host gallery, so behavior can be seen in motion, not just read about.
57
+ - **Randomness**: variation is unique per runtime by default (plain `Math.random()`); seeded, reproducible randomness is on the roadmap, not yet wired through.
64
58
 
65
- ### Visual layers — duration, scope, trigger
59
+ ## Getting started
66
60
 
67
- Use this frame when adding anything that moves, flickers, or spawns. **Preserve this taxonomy** when extending the library:
61
+ ```shell
62
+ npm i three-low-poly
63
+ ```
68
64
 
69
- | Category | Duration | Scope | Trigger | Examples |
70
- |----------|----------|-------|---------|----------|
71
- | **VFX / FX** | Short, often one-shot | Local to an event | `play()`, `trigger()` | Dust on landing, spell flash, impact sparks |
72
- | **Environment / weather** | Continuous | Scene or region | Always on (toggle optional) | Rain, fog, lightning, starfield |
73
- | **Ambient detail** | Continuous | Volume or prop-adjacent | Scene setup | Petals in a yard, bubbles in a jar |
65
+ Everything follows the same shape you already know from Three.js: construct with an options object, add it to the scene, and — if it animates — call `update(dt)` from your render loop.
74
66
 
75
- **Today:** Environment and ambience live under `src/effects/`. FX is roadmap-only; future FX will use `trigger()` + short lifetime, not only `update(dt)`.
67
+ ### Geometry
76
68
 
77
- | Export | Category | Notes |
78
- |--------|----------|-------|
79
- | `StarFieldEffect` | Environment | Sky dome; copy camera position each frame |
80
- | `RainEffect` | Environment | Regional rainfall volume |
81
- | `LightningEffect` | Environment | Drives `DirectionalLight`; expose `level` for fog/sky sync |
82
- | `GroundFogEffect` | Environment | Drifting mist cards; interior + optional perimeter patches |
83
- | `PetalDriftEffect` | Ambience | Scene volume or localized patch |
84
- | `WispEffect` | Ambience | Drifting will-o'-the-wisps in a bounded volume |
85
- | `EffervescenceEffect` | Ambience | Scale/position inside vessels; `spread` for round vessels |
86
- | `EmissivePulseEffect` | Ambience | Drives `emissiveIntensity` on existing materials (fake LED); no geometry |
87
- | `GlowHalo` | Ambience | Additive billboard glow; fake light without `PointLight` |
88
- | `FlameFlickerEffect` | Ambience | Sine flicker driver for halo, flame material, optional real light |
69
+ The primary intent of the library: parametric `BufferGeometry` subclasses you pair with your own material and mesh, exactly like Three.js's built-in primitives.
89
70
 
90
- ### Modeling conventions
71
+ ```ts
72
+ import { Mesh, MeshStandardMaterial } from "three";
73
+ import { StarGeometry } from "three-low-poly";
91
74
 
92
- - Prefer **vertex modeling** in geometry classes; use **geometry groups** for multi-material meshes.
93
- - Profile tools: splines, lathe, extrude, parabolic curves; NURBS is exploratory.
94
- - Use **geometry classes** where Three.js would use `BufferGeometry` subclasses.
95
- - Use **models** when the artifact is a stable prefab; use **factories** when the artifact is an assembly or a fill along extent.
75
+ const geometry = new StarGeometry({ points: 5, innerRadius: 0.5, outerRadius: 1 });
76
+ const material = new MeshStandardMaterial({ color: "#ffcc33", flatShading: true });
77
+ const star = new Mesh(geometry, material);
78
+ scene.add(star);
79
+ ```
96
80
 
97
- ### Glass and transparent contents
81
+ ### Prefab
98
82
 
99
- Transparent glass + liquid + instanced contents are a recurring pattern:
83
+ The same shape as a ready-made model geometry and materials already wired. Drop it straight into the scene.
100
84
 
101
- - Glass and liquid: `transparent: true`, `depthWrite: false`, `side: DoubleSide`
102
- - Draw order: contents liquid (`renderOrder` 1) → glass (`renderOrder` 2)
103
- - See `TestTubeRack`, effervescence example, and mad-science scene patterns
85
+ ```ts
86
+ import { Star } from "three-low-poly";
104
87
 
105
- ---
88
+ const star = new Star({ points: 5, color: "#ffcc33" });
89
+ scene.add(star);
90
+ ```
106
91
 
107
- ## Usage patterns
92
+ ### Factory
108
93
 
109
- ### Drop-in model
94
+ Assemble or scatter many parts from a single call.
110
95
 
111
96
  ```ts
112
- import { MossyRocks } from "three-low-poly";
97
+ import { scatterMossyRocks } from "three-low-poly";
113
98
 
114
- const rocks = new MossyRocks();
99
+ const rocks = scatterMossyRocks({ count: 12, width: 8, depth: 8, seed: 1337 });
115
100
  scene.add(rocks);
116
101
  ```
117
102
 
118
- ### Environment layer
103
+ ### Effect
104
+
105
+ Atmospheric layers animate off elapsed seconds — add them to the scene and call `update(dt)` each frame.
119
106
 
120
107
  ```ts
121
108
  import { RainEffect } from "three-low-poly";
@@ -123,234 +110,26 @@ import { RainEffect } from "three-low-poly";
123
110
  const rain = new RainEffect({ area: 12, height: 16, intensity: 0.45 });
124
111
  scene.add(rain);
125
112
 
126
- function animate(dt: number) {
113
+ function animate(dt) {
127
114
  rain.update(dt);
128
115
  renderer.render(scene, camera);
129
116
  }
130
117
  ```
131
118
 
132
- ### Lightning + synced atmosphere
133
-
134
- ```ts
135
- const bolt = new DirectionalLight(0xcdd8ff, 0);
136
- bolt.position.set(5, 12, -8);
137
- bolt.target.position.set(0, 2, 0);
138
- scene.add(bolt, bolt.target);
139
-
140
- const storm = new LightningEffect({ light: bolt, peak: 12, minGap: 3, maxGap: 9 });
141
-
142
- function animate(dt: number) {
143
- storm.update(dt);
144
- const flash = storm.level; // lerp fog, sky, emissive windows, rain intensity…
145
- }
146
- ```
147
-
148
- ### Ambience inside a prop
149
-
150
- ```ts
151
- const fizz = new EffervescenceEffect({ width: 1.2, height: 1.45, spread: 0.88 });
152
- fizz.position.set(0, 0.95, 0); // inside jar / flask volume
153
- scene.add(fizz);
154
- onFrame((dt) => fizz.update(dt));
155
- ```
156
-
157
- ### Star field sky dome
158
-
159
- ```ts
160
- const stars = new StarFieldEffect({ style: "burst", radius: 480, twinkle: true });
161
- scene.add(stars); // not parented to camera
162
-
163
- onFrame(() => {
164
- stars.position.copy(camera.position);
165
- stars.update();
166
- });
167
- ```
168
-
169
- ---
170
-
171
- ## Conventions (library code)
172
-
173
- | Topic | Convention |
174
- |-------|------------|
175
- | **Options** | `ThingOptions` interface; JSDoc on every option with defaults; export options types from `src/index.ts` |
176
- | **Classes** | Class-level JSDoc + `@example` for public effects and major models |
177
- | **Animation** | `update(dt: number)` — elapsed seconds |
178
- | **Disposal** | `dispose()` when owning geometry, materials, or textures |
179
- | **Instancing** | `DynamicDrawUsage`, reusable `Object3D` dummy, `Float32Array` state where hot |
180
- | **Examples** | Every feature has a corresponding file under `app/examples/`; auto-discovered by the host gallery |
181
- | **Documentation** | Parameter docs + minimal setup snippet in JSDoc — enough to wire without reading source |
182
- | **Randomness** | Unique per runtime today via `Math.random()`; seeded reproducibility is planned |
183
-
184
- **Particles in Three.js:** No built-in particle engine. Use `Points` + `PointsMaterial` for lightweight sprites, or `InstancedMesh` for streaks, bubbles, and petals. Choice is rendering strategy, not the environment/FX taxonomy.
185
-
186
- ---
187
-
188
- ## Current state
189
-
190
- What exists today and how mature each area is.
191
-
192
- | Area | Status |
193
- |------|--------|
194
- | **Models & geometry** | Broad catalog; vertex-first, grouped materials |
195
- | **Factories** | Book row, hex tiles, brick wall — ad hoc `...ByCount` / `...ByRadius` APIs |
196
- | **Environment effects** | Star field, rain, lightning, ground fog |
197
- | **Ambience effects** | Petal drift, effervescence, wisp, emissive pulse, glow halo, flame flicker |
198
- | **FX (burst / triggered)** | **Not implemented** |
199
- | **Animators** | Camera paths, light flicker, emissive pulse |
200
- | **Host gallery** | `app/examples/` — models, effects, scenes, reference |
201
- | **RNG** | `RandomNumberUtils` on bare `Math.random()` — **no seed in `src` today** |
202
- | **WebGPU / TSL** | Not yet; target for shader portability |
203
-
204
- ### Modeling (today)
205
-
206
- - Vertex-first geometry with **groups** for different materials
207
- - Profile primitives: spline, parabolic, lathe, extrude; NURBS exploratory
208
- - Geometry classes mirror Three.js `BufferGeometry` subclass patterns where possible
209
-
210
- ### Prefabs & instancing
211
-
212
- - **Prefabs** — prebuilt `Mesh` objects (`Jar`, `Tree`, `Mausoleum`, …)
213
- - **Instanced meshes** — tiles, books, particles, weather layers
214
-
215
- ### Performance (today)
216
-
217
- - Merged geometry and instancing where factories and effects allow
218
- - Batching audit and guidelines still informal — opportunity on roadmap
219
-
220
- ### Object options pattern
221
-
222
- Public APIs expose `ThingOptions` interfaces (exported alongside classes). Defaults live in destructuring; JSDoc documents each field. This is the standard extension point — no positional-arg sprawl.
223
-
224
- ---
225
-
226
- ## Design patterns (target)
227
-
228
- Two recurring design problems appear across factories and effects. The roadmap is to name them once and apply them everywhere — the difference between a library that feels **designed** and one that feels **accreted**.
229
-
230
- ### 1. Factory `fit` — constraint solving, not bespoke APIs
231
-
232
- Many factories are the **same operation under different pinned variables**. One invariant equation hides in each:
119
+ See the [example gallery](https://jasonsturges.com/three-low-poly/) for the full catalog of models, effects, and composed scenes. Local dev: `npm run host`.
233
120
 
234
- ```
235
- extent count × itemSize + gaps
236
- ```
237
-
238
- Three coupled variables — `extent`, `count`, `itemSize` — and a factory is just **pin two, solve for the third**:
239
-
240
- | Use case | Pinned | Solved |
241
- |----------|--------|--------|
242
- | “12 books on a 1-ft shelf” | count + extent | itemSize |
243
- | “1-inch books filling 1 ft” | itemSize + extent | count |
244
- | Hex tiles by-count vs by-radius | (either pair) | the third |
245
- | Fence by-path vs by-segment | (either pair) | the third |
246
-
247
- This is **constraint solving** — the same problem CSS flexbox, table layout, and bin-packing wrestle with. It only *feels* bespoke because each factory was written from scratch. It isn't; it's one template with a chosen pin. Existing `createHexagonalTilesByCount` / `createHexagonalTilesByRadius` and `rowOfBooksByCount` / `rowOfBooksByLength` are this pattern, discovered ad hoc.
248
-
249
- **Direction:** one factory per subject, discriminated `fit` union — replaces `...ByCount` / `...ByRadius` proliferation:
250
-
251
- ```ts
252
- type Fit =
253
- | { by: "count"; count: number } // pin count
254
- | { by: "size"; size: number } // pin item size
255
- | { by: "pack"; gap?: number } // greedy: as many as fit
256
- | { by: "pack-random"; sizeRange: [number, number]; seed?: number }; // stochastic fill
257
-
258
- createTileFloor({ width, depth, fit: { by: "count", count: 24 } });
259
- createBookRow({ length: 12, fit: { by: "pack-random", sizeRange: [0.8, 1.4], seed: 7 } });
260
- ```
261
-
262
- Why this over named-per-pin functions:
263
-
264
- - One import, one mental model, TypeScript exhaustiveness checking
265
- - **Randomness has a natural home** — random packing is a *fill strategy*, not a pinned variable; `fit` makes deterministic and stochastic fills peers
266
-
267
- **Goal:** share the same `fit` vocabulary across every fill-factory — books, fences, tiles, whatever's next.
268
-
269
- **Limits:** staggered / 2D packings (hex tiles are offset-stacked) need their own placement solver. The shared `fit` vocabulary targets **most 1D fills and simple grids**, not a universal packer.
270
-
271
- Emphasize **factories for assemblies** (racks, shelves, fences) over one-off geometry. Open design: `TestTube` vs liquid fill vs `TestTubeRack` as factory.
272
-
273
- ### 2. Seeded randomness — unique runtime, reproducible on demand
274
-
275
- Randomness is a **major** part of this library: unique experiences each runtime (randomly-sized packed books, scatter variation, stochastic fills). But repeatability matters too — debug a scene, share a seed, get the same shelf every reload.
276
-
277
- **Today:** `src/utils/RandomNumberUtils.ts` (`randomFloat`, `randomInteger`, `logarithmicRandomMax`, `logarithmicRandomMin`) all sit on bare `Math.random()`. This is a **from-scratch migration**, not finishing what's started.
278
-
279
- **Direction:**
280
-
281
- 1. Small fast seedable PRNG (`mulberry32` / `splitmix32`) → `() => number` stream from a seed
282
- 2. Re-express `RandomNumberUtils` to draw from an injected RNG stream (default = unseeded, existing call sites keep working)
283
- 3. Thread optional `seed` through every stochastic factory and effect (`pack-random`, scatter, variation, …)
284
- 4. Reproducibility goal: same options + same seed ⇒ byte-identical scene graph
285
-
286
- Default = fresh randomness each run. Pass `seed` to reproduce.
287
-
288
- ---
121
+ ![screen-capture](https://github.com/user-attachments/assets/d97345cc-bdaa-46d5-a267-531559919ee5)
122
+ _Example Graveyard scene_
289
123
 
290
124
  ## Roadmap
291
125
 
292
- Priorities and direction — not a commitment schedule.
293
-
294
- ### Atmosphere & content
295
-
296
- - More **environment** layers (fog drivers, ground mist, wind fields)
297
- - More **ambience** (leaves on terrain, localized scatter)
298
- - **FX** module: short-lived bursts, `trigger()`, pools, fade-out
299
- - Example `meta.category` tags (`environment` | `ambience` | `fx`) for gallery clarity
300
- - Optional folder split: `environment/` / `ambience/` / `fx/` when FX lands
301
-
302
- ### Randomness & reproducibility
303
-
304
- See [Seeded randomness](#2-seeded-randomness--unique-runtime-reproducible-on-demand) above. Highest-leverage migration in the random story.
305
-
306
- ### Factories
307
-
308
- - Unified `fit` discriminant across book, tile, fence, and future fill factories
309
- - More assembly factories (racks, shelves, composite props)
310
- - Constraint-solving template instead of one-off `...ByX` functions
311
-
312
- ### Rendering & platform
313
-
314
- - **WebGPU** with **TSL** shaders where possible — WebGL + WebGPU compatibility from one shader source
315
- - Glassware material pass (`DoubleSide`, `depthWrite`, `renderOrder`) across bottles and science glass
316
- - Batching audit: merge opportunities, instancing guidelines
317
-
318
- ### Geometry & scenes (exploratory)
319
-
320
- - Diorama / room shells with windows (geometry exists; not yet library-ready)
321
- - Leaded lattice windows, terrain, water
322
- - NURBS / advanced profiles where they earn their complexity
323
-
324
- ### Documentation
325
-
326
- - This README as single source of truth — no separate `docs/` folder
327
- - Per-class JSDoc remains the API contract
328
- - Examples remain living documentation
329
-
330
- ---
331
-
332
- ## Getting started
333
-
334
- ```shell
335
- npm i three-low-poly
336
- ```
337
-
338
- ```js
339
- import { MossyRocks } from "three-low-poly";
340
-
341
- const rocks = new MossyRocks();
342
- scene.add(rocks);
343
- ```
344
-
345
- **Examples:** [jasonsturges.com/three-low-poly/](https://jasonsturges.com/three-low-poly/) — browse by category in the host gallery. Local dev: `npm run host`.
346
-
347
- **Build:** Library publishes from `dist/`; examples live in `app/` and import `three-low-poly` like a consumer would.
348
-
349
- ![screen-capture](https://github.com/user-attachments/assets/d97345cc-bdaa-46d5-a267-531559919ee5)
350
- _Example Graveyard scene_
126
+ Direction, not a committed schedule:
351
127
 
352
- ---
128
+ - **Seeded randomness**: thread an optional seed through stochastic factories and effects so a scene can be reproduced exactly, while staying unique by default when no seed is given.
129
+ - **Unified fill API**: collapse the growing set of `...ByCount` / `...ByRadius` factory variants into a single discriminated option (fit by count, by item size, or by greedy/random packing), so the same vocabulary applies across books, tiles, fences, and whatever comes next.
130
+ - **WebGPU + TSL**: shaders authored once via Three Shading Language, portable across WebGL and WebGPU.
131
+ - **FX module**: short-lived, triggered bursts (impact dust, spell flashes) as a category distinct from the always-on environment and ambience layers that exist today.
353
132
 
354
133
  ## Author
355
134
 
356
- Jason Sturges procedural low-poly tooling for Three.js.
135
+ Jason Sturges, procedural low-poly tooling for Three.js.