@lovo/matter 0.2.0 → 0.4.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/dist/index.d.cts CHANGED
@@ -1,8 +1,8 @@
1
- import { WebGPURenderer, Node } from 'three/webgpu';
2
1
  import { Color } from 'three';
2
+ import { WebGPURenderer, Node } from 'three/webgpu';
3
3
  import { ShaderNodeObject } from 'three/tsl';
4
4
 
5
- type MatterBackend = 'webgpu' | 'webgl2';
5
+ type GpuBackend = 'webgpu' | 'webgl2';
6
6
  interface CreateRendererOptions {
7
7
  /** Anti-alias the framebuffer. Default: true. */
8
8
  antialias?: boolean;
@@ -15,11 +15,11 @@ interface CreateRendererOptions {
15
15
  /** Cap on devicePixelRatio. Default: 2. Pass Infinity to disable. */
16
16
  maxDPR?: number;
17
17
  }
18
- interface MatterRenderer {
18
+ interface GpuRenderer {
19
19
  /** The underlying Three.js WebGPURenderer (which may be running on a WebGL2 backend). */
20
20
  three: WebGPURenderer;
21
21
  /** Which backend the renderer initialized with. */
22
- backend: MatterBackend;
22
+ backend: GpuBackend;
23
23
  /** Tear down the renderer and release GPU resources. */
24
24
  dispose: () => void;
25
25
  /** Resize the renderer to the canvas's current client dimensions. */
@@ -32,57 +32,7 @@ interface MatterRenderer {
32
32
  * unavailable on the host. The returned object exposes the underlying
33
33
  * three renderer plus a small wrapper for resize and disposal.
34
34
  */
35
- declare function createRenderer(canvas: HTMLCanvasElement, opts?: CreateRendererOptions): Promise<MatterRenderer>;
36
-
37
- interface SchedulerTick {
38
- /** Seconds since the previous tick. 0 on the first call. */
39
- delta: number;
40
- /** Total seconds since the scheduler started its current run. */
41
- elapsed: number;
42
- /** The raw `performance.now()` timestamp the rAF callback received. */
43
- now: number;
44
- }
45
- type SchedulerClient = (tick: SchedulerTick) => void;
46
- /**
47
- * Batches `requestAnimationFrame` calls across all clients registered with
48
- * a single scheduler. One scheduler is created per <MatterScene>; clients
49
- * are typically a Three.js renderer's render call.
50
- */
51
- declare class MatterScheduler {
52
- private readonly clients;
53
- private rafId;
54
- private running;
55
- private paused;
56
- private idle;
57
- private flushPending;
58
- private startedAt;
59
- private lastTickAt;
60
- /** Activate the scheduler. The rAF loop starts on the first client added. */
61
- start(): void;
62
- /** Halt the rAF loop entirely. Use dispose() for permanent teardown. */
63
- stop(): void;
64
- /** Temporarily skip ticks without losing client registrations. */
65
- pause(): void;
66
- /** Resume after pause(). */
67
- resume(): void;
68
- /** Register a client to be called every frame. */
69
- add(client: SchedulerClient): void;
70
- /** Unregister a client. */
71
- remove(client: SchedulerClient): void;
72
- /** Permanent teardown: stop the loop and drop all clients. */
73
- dispose(): void;
74
- /**
75
- * Mark the scheduler idle. The next tick still fires (a final flush so
76
- * uniform changes that triggered the idle state are rendered), then the
77
- * rAF loop halts. Use `requestRender()` or `setIdle(false)` to wake.
78
- */
79
- setIdle(idle: boolean): void;
80
- /** Force a single tick while idle. Useful for prop-change invalidation. */
81
- requestRender(): void;
82
- private maybeQueue;
83
- private cancel;
84
- private readonly frame;
85
- }
35
+ declare function createRenderer(canvas: HTMLCanvasElement, opts?: CreateRendererOptions): Promise<GpuRenderer>;
86
36
 
87
37
  type Vec2 = readonly [number, number];
88
38
  interface CursorInputOptions {
@@ -122,7 +72,7 @@ interface CursorInputOptions {
122
72
  type ChangeListener = (value: Vec2) => void;
123
73
  /**
124
74
  * Smoothed pointer tracker emitting a normalized (0..1) Vec2 position.
125
- * Implements the MatterSignal protocol (`get()` + `on('change', cb)`)
75
+ * Implements the AnimatableSignal protocol (`get()` + `on('change', cb)`)
126
76
  * so it composes with Motion's `useTransform` and similar tools.
127
77
  */
128
78
  declare class CursorInput {
@@ -136,7 +86,7 @@ declare class CursorInput {
136
86
  private readonly handleMouseMove;
137
87
  private disposed;
138
88
  constructor(opts?: CursorInputOptions);
139
- /** Current smoothed position. Implements MatterSignal protocol. */
89
+ /** Current smoothed position. Implements AnimatableSignal protocol. */
140
90
  get(): Vec2;
141
91
  /** Subscribe to change events. Returns an unsubscribe function. */
142
92
  on(_event: 'change', cb: ChangeListener): () => void;
@@ -308,6 +258,39 @@ declare function cursorRipple(p: TSLNode, center: TSLNode, opts?: CursorRippleOp
308
258
 
309
259
  declare const time: ShaderNodeObject<Node>;
310
260
 
261
+ /**
262
+ * Hash-based film grain — chaotic, uncorrelated per-pixel noise sampled
263
+ * from `uvNode`. The output is *centered* around zero so it acts as a
264
+ * brightness-preserving texture overlay (half the pixels brighten by up
265
+ * to `intensity`, half darken, mean unchanged). ADD the result to a color.
266
+ *
267
+ * filmGrain(uv, k) → static grain
268
+ * filmGrain(uv, k, time) → twinkling grain. Pass a quantized time node
269
+ * (e.g. `time.mul(speed).mul(60).floor()`) so
270
+ * the grain re-randomizes at a controllable
271
+ * "shutter rate" instead of every frame.
272
+ *
273
+ * Recipe:
274
+ *
275
+ * base = vec2(uv·c1, uv·c2) + timeOffset
276
+ * hash = fract(sin(base) * 43758.5453)
277
+ * out = (length(hash) - 0.765) * intensity
278
+ *
279
+ * `c1 = (2127.1, 81.17)` and `c2 = (1269.5, 283.37)` are arbitrary
280
+ * near-prime constants that produce visually-uncorrelated noise. `0.765`
281
+ * is the empirical mean of `length(vec2(u, v))` for uniform u, v ∈ [0, 1),
282
+ * computed once so we don't have to subtract it at runtime per pixel.
283
+ *
284
+ * For a film-stock look (darkens as grain rises — silver-emulsion
285
+ * physics) subtract the result from the color instead of adding.
286
+ *
287
+ * @param uvNode vec2 TSL node, typically `uv()`.
288
+ * @param intensity number or TSL node in [0, 1]; scales the grain.
289
+ * @param timeOffset optional number or TSL node added to each sample
290
+ * before hashing. `0` (default) → static grain.
291
+ */
292
+ declare function filmGrain(uvNode: ShaderNodeObject<Node>, intensity: ShaderNodeObject<Node> | number, timeOffset?: ShaderNodeObject<Node> | number): ShaderNodeObject<Node>;
293
+
311
294
  type ReducedMotionPolicy = 'auto' | 'off' | 'slow' | 'paused';
312
295
  /**
313
296
  * Public surface exposed to package consumers. `recompute` is intentionally
@@ -374,4 +357,54 @@ interface IntersectionWatcher {
374
357
  */
375
358
  declare function createIntersectionWatcher(canvas: HTMLCanvasElement): IntersectionWatcher;
376
359
 
377
- export { type ColorRampStop, type CreateRendererOptions, CursorInput, type CursorInputOptions, type CursorRippleOptions, type FBMOptions, type IntersectionWatcher, type MatterBackend, type MatterRenderer, MatterScheduler, type ReducedMotionPolicy, type ReducedMotionWatcher, type SchedulerClient, type SchedulerTick, type TSLNode, type Vec2, type VisibilityWatcher, colorRamp, createIntersectionWatcher, createReducedMotionWatcher, createRenderer, createVisibilityWatcher, cursorRipple, displace, fbm, getReducedMotionPolicy, getReducedMotionTimeScale, noise, quantize, sdfCircle, setReducedMotionPolicy, time, voronoi };
360
+ interface SchedulerTick {
361
+ /** Seconds since the previous tick. 0 on the first call. */
362
+ delta: number;
363
+ /** Total seconds since the scheduler started its current run. */
364
+ elapsed: number;
365
+ /** The raw `performance.now()` timestamp the rAF callback received. */
366
+ now: number;
367
+ }
368
+ type SchedulerClient = (tick: SchedulerTick) => void;
369
+ /**
370
+ * Batches `requestAnimationFrame` calls across all clients registered with
371
+ * a single scheduler. One scheduler is created per <ShaderScene>; clients
372
+ * are typically a Three.js renderer's render call.
373
+ */
374
+ declare class FrameScheduler {
375
+ private readonly clients;
376
+ private rafId;
377
+ private running;
378
+ private paused;
379
+ private idle;
380
+ private flushPending;
381
+ private startedAt;
382
+ private lastTickAt;
383
+ /** Activate the scheduler. The rAF loop starts on the first client added. */
384
+ start(): void;
385
+ /** Halt the rAF loop entirely. Use dispose() for permanent teardown. */
386
+ stop(): void;
387
+ /** Temporarily skip ticks without losing client registrations. */
388
+ pause(): void;
389
+ /** Resume after pause(). */
390
+ resume(): void;
391
+ /** Register a client to be called every frame. */
392
+ add(client: SchedulerClient): void;
393
+ /** Unregister a client. */
394
+ remove(client: SchedulerClient): void;
395
+ /** Permanent teardown: stop the loop and drop all clients. */
396
+ dispose(): void;
397
+ /**
398
+ * Mark the scheduler idle. The next tick still fires (a final flush so
399
+ * uniform changes that triggered the idle state are rendered), then the
400
+ * rAF loop halts. Use `requestRender()` or `setIdle(false)` to wake.
401
+ */
402
+ setIdle(idle: boolean): void;
403
+ /** Force a single tick while idle. Useful for prop-change invalidation. */
404
+ requestRender(): void;
405
+ private maybeQueue;
406
+ private cancel;
407
+ private readonly frame;
408
+ }
409
+
410
+ export { type ColorRampStop, type CreateRendererOptions, CursorInput, type CursorInputOptions, type CursorRippleOptions, type FBMOptions, FrameScheduler, type GpuBackend, type GpuRenderer, type IntersectionWatcher, type ReducedMotionPolicy, type ReducedMotionWatcher, type SchedulerClient, type SchedulerTick, type TSLNode, type Vec2, type VisibilityWatcher, colorRamp, createIntersectionWatcher, createReducedMotionWatcher, createRenderer, createVisibilityWatcher, cursorRipple, displace, fbm, filmGrain, getReducedMotionPolicy, getReducedMotionTimeScale, noise, quantize, sdfCircle, setReducedMotionPolicy, time, voronoi };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { WebGPURenderer, Node } from 'three/webgpu';
2
1
  import { Color } from 'three';
2
+ import { WebGPURenderer, Node } from 'three/webgpu';
3
3
  import { ShaderNodeObject } from 'three/tsl';
4
4
 
5
- type MatterBackend = 'webgpu' | 'webgl2';
5
+ type GpuBackend = 'webgpu' | 'webgl2';
6
6
  interface CreateRendererOptions {
7
7
  /** Anti-alias the framebuffer. Default: true. */
8
8
  antialias?: boolean;
@@ -15,11 +15,11 @@ interface CreateRendererOptions {
15
15
  /** Cap on devicePixelRatio. Default: 2. Pass Infinity to disable. */
16
16
  maxDPR?: number;
17
17
  }
18
- interface MatterRenderer {
18
+ interface GpuRenderer {
19
19
  /** The underlying Three.js WebGPURenderer (which may be running on a WebGL2 backend). */
20
20
  three: WebGPURenderer;
21
21
  /** Which backend the renderer initialized with. */
22
- backend: MatterBackend;
22
+ backend: GpuBackend;
23
23
  /** Tear down the renderer and release GPU resources. */
24
24
  dispose: () => void;
25
25
  /** Resize the renderer to the canvas's current client dimensions. */
@@ -32,57 +32,7 @@ interface MatterRenderer {
32
32
  * unavailable on the host. The returned object exposes the underlying
33
33
  * three renderer plus a small wrapper for resize and disposal.
34
34
  */
35
- declare function createRenderer(canvas: HTMLCanvasElement, opts?: CreateRendererOptions): Promise<MatterRenderer>;
36
-
37
- interface SchedulerTick {
38
- /** Seconds since the previous tick. 0 on the first call. */
39
- delta: number;
40
- /** Total seconds since the scheduler started its current run. */
41
- elapsed: number;
42
- /** The raw `performance.now()` timestamp the rAF callback received. */
43
- now: number;
44
- }
45
- type SchedulerClient = (tick: SchedulerTick) => void;
46
- /**
47
- * Batches `requestAnimationFrame` calls across all clients registered with
48
- * a single scheduler. One scheduler is created per <MatterScene>; clients
49
- * are typically a Three.js renderer's render call.
50
- */
51
- declare class MatterScheduler {
52
- private readonly clients;
53
- private rafId;
54
- private running;
55
- private paused;
56
- private idle;
57
- private flushPending;
58
- private startedAt;
59
- private lastTickAt;
60
- /** Activate the scheduler. The rAF loop starts on the first client added. */
61
- start(): void;
62
- /** Halt the rAF loop entirely. Use dispose() for permanent teardown. */
63
- stop(): void;
64
- /** Temporarily skip ticks without losing client registrations. */
65
- pause(): void;
66
- /** Resume after pause(). */
67
- resume(): void;
68
- /** Register a client to be called every frame. */
69
- add(client: SchedulerClient): void;
70
- /** Unregister a client. */
71
- remove(client: SchedulerClient): void;
72
- /** Permanent teardown: stop the loop and drop all clients. */
73
- dispose(): void;
74
- /**
75
- * Mark the scheduler idle. The next tick still fires (a final flush so
76
- * uniform changes that triggered the idle state are rendered), then the
77
- * rAF loop halts. Use `requestRender()` or `setIdle(false)` to wake.
78
- */
79
- setIdle(idle: boolean): void;
80
- /** Force a single tick while idle. Useful for prop-change invalidation. */
81
- requestRender(): void;
82
- private maybeQueue;
83
- private cancel;
84
- private readonly frame;
85
- }
35
+ declare function createRenderer(canvas: HTMLCanvasElement, opts?: CreateRendererOptions): Promise<GpuRenderer>;
86
36
 
87
37
  type Vec2 = readonly [number, number];
88
38
  interface CursorInputOptions {
@@ -122,7 +72,7 @@ interface CursorInputOptions {
122
72
  type ChangeListener = (value: Vec2) => void;
123
73
  /**
124
74
  * Smoothed pointer tracker emitting a normalized (0..1) Vec2 position.
125
- * Implements the MatterSignal protocol (`get()` + `on('change', cb)`)
75
+ * Implements the AnimatableSignal protocol (`get()` + `on('change', cb)`)
126
76
  * so it composes with Motion's `useTransform` and similar tools.
127
77
  */
128
78
  declare class CursorInput {
@@ -136,7 +86,7 @@ declare class CursorInput {
136
86
  private readonly handleMouseMove;
137
87
  private disposed;
138
88
  constructor(opts?: CursorInputOptions);
139
- /** Current smoothed position. Implements MatterSignal protocol. */
89
+ /** Current smoothed position. Implements AnimatableSignal protocol. */
140
90
  get(): Vec2;
141
91
  /** Subscribe to change events. Returns an unsubscribe function. */
142
92
  on(_event: 'change', cb: ChangeListener): () => void;
@@ -308,6 +258,39 @@ declare function cursorRipple(p: TSLNode, center: TSLNode, opts?: CursorRippleOp
308
258
 
309
259
  declare const time: ShaderNodeObject<Node>;
310
260
 
261
+ /**
262
+ * Hash-based film grain — chaotic, uncorrelated per-pixel noise sampled
263
+ * from `uvNode`. The output is *centered* around zero so it acts as a
264
+ * brightness-preserving texture overlay (half the pixels brighten by up
265
+ * to `intensity`, half darken, mean unchanged). ADD the result to a color.
266
+ *
267
+ * filmGrain(uv, k) → static grain
268
+ * filmGrain(uv, k, time) → twinkling grain. Pass a quantized time node
269
+ * (e.g. `time.mul(speed).mul(60).floor()`) so
270
+ * the grain re-randomizes at a controllable
271
+ * "shutter rate" instead of every frame.
272
+ *
273
+ * Recipe:
274
+ *
275
+ * base = vec2(uv·c1, uv·c2) + timeOffset
276
+ * hash = fract(sin(base) * 43758.5453)
277
+ * out = (length(hash) - 0.765) * intensity
278
+ *
279
+ * `c1 = (2127.1, 81.17)` and `c2 = (1269.5, 283.37)` are arbitrary
280
+ * near-prime constants that produce visually-uncorrelated noise. `0.765`
281
+ * is the empirical mean of `length(vec2(u, v))` for uniform u, v ∈ [0, 1),
282
+ * computed once so we don't have to subtract it at runtime per pixel.
283
+ *
284
+ * For a film-stock look (darkens as grain rises — silver-emulsion
285
+ * physics) subtract the result from the color instead of adding.
286
+ *
287
+ * @param uvNode vec2 TSL node, typically `uv()`.
288
+ * @param intensity number or TSL node in [0, 1]; scales the grain.
289
+ * @param timeOffset optional number or TSL node added to each sample
290
+ * before hashing. `0` (default) → static grain.
291
+ */
292
+ declare function filmGrain(uvNode: ShaderNodeObject<Node>, intensity: ShaderNodeObject<Node> | number, timeOffset?: ShaderNodeObject<Node> | number): ShaderNodeObject<Node>;
293
+
311
294
  type ReducedMotionPolicy = 'auto' | 'off' | 'slow' | 'paused';
312
295
  /**
313
296
  * Public surface exposed to package consumers. `recompute` is intentionally
@@ -374,4 +357,54 @@ interface IntersectionWatcher {
374
357
  */
375
358
  declare function createIntersectionWatcher(canvas: HTMLCanvasElement): IntersectionWatcher;
376
359
 
377
- export { type ColorRampStop, type CreateRendererOptions, CursorInput, type CursorInputOptions, type CursorRippleOptions, type FBMOptions, type IntersectionWatcher, type MatterBackend, type MatterRenderer, MatterScheduler, type ReducedMotionPolicy, type ReducedMotionWatcher, type SchedulerClient, type SchedulerTick, type TSLNode, type Vec2, type VisibilityWatcher, colorRamp, createIntersectionWatcher, createReducedMotionWatcher, createRenderer, createVisibilityWatcher, cursorRipple, displace, fbm, getReducedMotionPolicy, getReducedMotionTimeScale, noise, quantize, sdfCircle, setReducedMotionPolicy, time, voronoi };
360
+ interface SchedulerTick {
361
+ /** Seconds since the previous tick. 0 on the first call. */
362
+ delta: number;
363
+ /** Total seconds since the scheduler started its current run. */
364
+ elapsed: number;
365
+ /** The raw `performance.now()` timestamp the rAF callback received. */
366
+ now: number;
367
+ }
368
+ type SchedulerClient = (tick: SchedulerTick) => void;
369
+ /**
370
+ * Batches `requestAnimationFrame` calls across all clients registered with
371
+ * a single scheduler. One scheduler is created per <ShaderScene>; clients
372
+ * are typically a Three.js renderer's render call.
373
+ */
374
+ declare class FrameScheduler {
375
+ private readonly clients;
376
+ private rafId;
377
+ private running;
378
+ private paused;
379
+ private idle;
380
+ private flushPending;
381
+ private startedAt;
382
+ private lastTickAt;
383
+ /** Activate the scheduler. The rAF loop starts on the first client added. */
384
+ start(): void;
385
+ /** Halt the rAF loop entirely. Use dispose() for permanent teardown. */
386
+ stop(): void;
387
+ /** Temporarily skip ticks without losing client registrations. */
388
+ pause(): void;
389
+ /** Resume after pause(). */
390
+ resume(): void;
391
+ /** Register a client to be called every frame. */
392
+ add(client: SchedulerClient): void;
393
+ /** Unregister a client. */
394
+ remove(client: SchedulerClient): void;
395
+ /** Permanent teardown: stop the loop and drop all clients. */
396
+ dispose(): void;
397
+ /**
398
+ * Mark the scheduler idle. The next tick still fires (a final flush so
399
+ * uniform changes that triggered the idle state are rendered), then the
400
+ * rAF loop halts. Use `requestRender()` or `setIdle(false)` to wake.
401
+ */
402
+ setIdle(idle: boolean): void;
403
+ /** Force a single tick while idle. Useful for prop-change invalidation. */
404
+ requestRender(): void;
405
+ private maybeQueue;
406
+ private cancel;
407
+ private readonly frame;
408
+ }
409
+
410
+ export { type ColorRampStop, type CreateRendererOptions, CursorInput, type CursorInputOptions, type CursorRippleOptions, type FBMOptions, FrameScheduler, type GpuBackend, type GpuRenderer, type IntersectionWatcher, type ReducedMotionPolicy, type ReducedMotionWatcher, type SchedulerClient, type SchedulerTick, type TSLNode, type Vec2, type VisibilityWatcher, colorRamp, createIntersectionWatcher, createReducedMotionWatcher, createRenderer, createVisibilityWatcher, cursorRipple, displace, fbm, filmGrain, getReducedMotionPolicy, getReducedMotionTimeScale, noise, quantize, sdfCircle, setReducedMotionPolicy, time, voronoi };