@basmilius/sparkle 2.0.0 → 2.2.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.
Files changed (129) hide show
  1. package/dist/index.d.mts +1053 -28
  2. package/dist/index.d.mts.map +1 -1
  3. package/dist/index.mjs +4840 -400
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +7 -2
  6. package/src/aurora/consts.ts +3 -0
  7. package/src/aurora/index.ts +10 -0
  8. package/src/aurora/layer.ts +180 -0
  9. package/src/aurora/types.ts +13 -0
  10. package/src/balloons/consts.ts +3 -0
  11. package/src/balloons/index.ts +12 -0
  12. package/src/balloons/layer.ts +169 -0
  13. package/src/balloons/particle.ts +110 -0
  14. package/src/balloons/types.ts +14 -0
  15. package/src/bubbles/consts.ts +3 -0
  16. package/src/bubbles/index.ts +10 -0
  17. package/src/bubbles/layer.ts +246 -0
  18. package/src/bubbles/types.ts +21 -0
  19. package/src/canvas.ts +32 -1
  20. package/src/color.ts +19 -0
  21. package/src/confetti/consts.ts +13 -13
  22. package/src/confetti/index.ts +20 -2
  23. package/src/confetti/layer.ts +155 -0
  24. package/src/confetti/particle.ts +106 -0
  25. package/src/confetti/shapes.ts +104 -0
  26. package/src/confetti/types.ts +4 -1
  27. package/src/distance.ts +1 -1
  28. package/src/donuts/consts.ts +19 -0
  29. package/src/donuts/donut.ts +12 -0
  30. package/src/donuts/index.ts +9 -0
  31. package/src/donuts/layer.ts +301 -0
  32. package/src/effect.ts +107 -0
  33. package/src/fade.ts +87 -0
  34. package/src/fireflies/consts.ts +3 -0
  35. package/src/fireflies/index.ts +12 -0
  36. package/src/fireflies/layer.ts +169 -0
  37. package/src/fireflies/particle.ts +124 -0
  38. package/src/fireflies/types.ts +17 -0
  39. package/src/firepit/consts.ts +3 -0
  40. package/src/firepit/index.ts +10 -0
  41. package/src/firepit/layer.ts +193 -0
  42. package/src/firepit/types.ts +20 -0
  43. package/src/fireworks/create-explosion.ts +237 -0
  44. package/src/fireworks/explosion.ts +9 -9
  45. package/src/fireworks/firework.ts +9 -8
  46. package/src/fireworks/index.ts +19 -3
  47. package/src/fireworks/layer.ts +203 -0
  48. package/src/fireworks/spark.ts +9 -9
  49. package/src/fireworks/types.ts +2 -2
  50. package/src/glitter/consts.ts +13 -0
  51. package/src/glitter/index.ts +9 -0
  52. package/src/glitter/layer.ts +181 -0
  53. package/src/glitter/types.ts +33 -0
  54. package/src/index.ts +27 -0
  55. package/src/lanterns/consts.ts +13 -0
  56. package/src/lanterns/index.ts +9 -0
  57. package/src/lanterns/layer.ts +178 -0
  58. package/src/lanterns/types.ts +22 -0
  59. package/src/layer.ts +26 -0
  60. package/src/leaves/consts.ts +16 -0
  61. package/src/leaves/index.ts +9 -0
  62. package/src/leaves/layer.ts +258 -0
  63. package/src/leaves/types.ts +25 -0
  64. package/src/lightning/consts.ts +3 -0
  65. package/src/lightning/index.ts +11 -0
  66. package/src/lightning/layer.ts +41 -0
  67. package/src/lightning/system.ts +196 -0
  68. package/src/lightning/types.ts +20 -0
  69. package/src/matrix/consts.ts +5 -0
  70. package/src/matrix/index.ts +9 -0
  71. package/src/matrix/layer.ts +154 -0
  72. package/src/matrix/types.ts +17 -0
  73. package/src/orbits/consts.ts +13 -0
  74. package/src/orbits/index.ts +9 -0
  75. package/src/orbits/layer.ts +213 -0
  76. package/src/orbits/types.ts +27 -0
  77. package/src/particles/consts.ts +3 -0
  78. package/src/particles/index.ts +10 -0
  79. package/src/particles/layer.ts +360 -0
  80. package/src/particles/types.ts +10 -0
  81. package/src/petals/consts.ts +13 -0
  82. package/src/petals/index.ts +10 -0
  83. package/src/petals/layer.ts +174 -0
  84. package/src/petals/types.ts +15 -0
  85. package/src/plasma/consts.ts +3 -0
  86. package/src/plasma/index.ts +10 -0
  87. package/src/plasma/layer.ts +107 -0
  88. package/src/plasma/types.ts +5 -0
  89. package/src/rain/consts.ts +3 -0
  90. package/src/rain/index.ts +12 -0
  91. package/src/rain/layer.ts +194 -0
  92. package/src/rain/particle.ts +132 -0
  93. package/src/rain/types.ts +22 -0
  94. package/src/sandstorm/consts.ts +3 -0
  95. package/src/sandstorm/index.ts +10 -0
  96. package/src/sandstorm/layer.ts +152 -0
  97. package/src/sandstorm/types.ts +10 -0
  98. package/src/scene.ts +201 -0
  99. package/src/shooting-stars/index.ts +3 -0
  100. package/src/shooting-stars/system.ts +151 -0
  101. package/src/shooting-stars/types.ts +11 -0
  102. package/src/simulation-canvas.ts +83 -0
  103. package/src/snow/consts.ts +2 -2
  104. package/src/snow/index.ts +9 -2
  105. package/src/snow/{simulation.ts → layer.ts} +64 -89
  106. package/src/sparklers/consts.ts +3 -0
  107. package/src/sparklers/index.ts +16 -0
  108. package/src/sparklers/layer.ts +220 -0
  109. package/src/sparklers/particle.ts +89 -0
  110. package/src/sparklers/types.ts +13 -0
  111. package/src/stars/consts.ts +3 -0
  112. package/src/stars/index.ts +10 -0
  113. package/src/stars/layer.ts +139 -0
  114. package/src/stars/types.ts +12 -0
  115. package/src/streamers/consts.ts +14 -0
  116. package/src/streamers/index.ts +10 -0
  117. package/src/streamers/layer.ts +223 -0
  118. package/src/streamers/types.ts +14 -0
  119. package/src/trail.ts +140 -0
  120. package/src/waves/consts.ts +3 -0
  121. package/src/waves/index.ts +10 -0
  122. package/src/waves/layer.ts +164 -0
  123. package/src/waves/types.ts +10 -0
  124. package/src/wormhole/consts.ts +3 -0
  125. package/src/wormhole/index.ts +10 -0
  126. package/src/wormhole/layer.ts +197 -0
  127. package/src/wormhole/types.ts +10 -0
  128. package/src/confetti/simulation.ts +0 -221
  129. package/src/fireworks/simulation.ts +0 -493
package/src/scene.ts ADDED
@@ -0,0 +1,201 @@
1
+ import { LimitedFrameRateCanvas } from './canvas';
2
+ import { applyEdgeFade } from './fade';
3
+ import type { SimulationLayer } from './layer';
4
+
5
+ /**
6
+ * Internal canvas runner that drives all layers in a Scene.
7
+ */
8
+ class SceneCanvas extends LimitedFrameRateCanvas {
9
+ readonly #layers: SimulationLayer[];
10
+ readonly #contextOptions: CanvasRenderingContext2DSettings;
11
+ #offscreen: HTMLCanvasElement | null = null;
12
+ #offscreenCtx: CanvasRenderingContext2D | null = null;
13
+
14
+ constructor(canvas: HTMLCanvasElement, layers: SimulationLayer[], frameRate: number, options: CanvasRenderingContext2DSettings) {
15
+ super(canvas, frameRate, options);
16
+ this.#layers = layers;
17
+ this.#contextOptions = options;
18
+
19
+ canvas.style.position = 'absolute';
20
+ canvas.style.top = '0';
21
+ canvas.style.left = '0';
22
+ canvas.style.height = '100%';
23
+ canvas.style.width = '100%';
24
+ }
25
+
26
+ start(): void {
27
+ for (const layer of this.#layers) {
28
+ layer.onMount(this.canvas);
29
+ }
30
+ super.start();
31
+ }
32
+
33
+ destroy(): void {
34
+ for (const layer of this.#layers) {
35
+ layer.onUnmount(this.canvas);
36
+ }
37
+ super.destroy();
38
+ }
39
+
40
+ draw(): void {
41
+ this.canvas.height = this.height;
42
+ this.canvas.width = this.width;
43
+
44
+ const ctx = this.context;
45
+ ctx.clearRect(0, 0, this.width, this.height);
46
+
47
+ for (const layer of this.#layers) {
48
+ if (layer.fade) {
49
+ const offCtx = this.#getOffscreenCtx(this.width, this.height);
50
+ offCtx.clearRect(0, 0, this.width, this.height);
51
+ layer.draw(offCtx, this.width, this.height);
52
+ applyEdgeFade(offCtx, this.width, this.height, layer.fade);
53
+ ctx.drawImage(this.#offscreen!, 0, 0);
54
+ } else {
55
+ ctx.save();
56
+ layer.draw(ctx, this.width, this.height);
57
+ ctx.restore();
58
+ }
59
+ }
60
+ }
61
+
62
+ tick(): void {
63
+ const dt = (this.delta > 0 && this.delta < 200 ? this.delta / (1000 / 60) : 1) * this.speed * LimitedFrameRateCanvas.globalSpeed;
64
+
65
+ for (const layer of this.#layers) {
66
+ layer.tick(dt, this.width, this.height);
67
+ }
68
+ }
69
+
70
+ onResize(): void {
71
+ super.onResize();
72
+
73
+ if (this.#offscreen) {
74
+ this.#offscreen.width = this.width;
75
+ this.#offscreen.height = this.height;
76
+ }
77
+
78
+ for (const layer of this.#layers) {
79
+ layer.onResize(this.width, this.height);
80
+ }
81
+ }
82
+
83
+ #getOffscreenCtx(width: number, height: number): CanvasRenderingContext2D {
84
+ if (!this.#offscreen) {
85
+ this.#offscreen = document.createElement('canvas');
86
+ this.#offscreen.width = width;
87
+ this.#offscreen.height = height;
88
+ this.#offscreenCtx = this.#offscreen.getContext('2d', this.#contextOptions)!;
89
+ }
90
+
91
+ return this.#offscreenCtx!;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Composable canvas that renders multiple Effect layers in order (first = bottom, last = top).
97
+ *
98
+ * @example
99
+ * const scene = new Scene()
100
+ * .mount(canvas)
101
+ * .layer(new Aurora({ bands: 5 }))
102
+ * .layer(new Stars().withFade({ bottom: 0.4 }))
103
+ * .start();
104
+ */
105
+ export class Scene {
106
+ readonly #layers: SimulationLayer[] = [];
107
+ readonly #frameRate: number;
108
+ readonly #defaultOptions: CanvasRenderingContext2DSettings;
109
+ #runner: SceneCanvas | null = null;
110
+
111
+ constructor(frameRate: number = 60, options: CanvasRenderingContext2DSettings = {colorSpace: 'display-p3'}) {
112
+ this.#frameRate = frameRate;
113
+ this.#defaultOptions = options;
114
+ }
115
+
116
+ /**
117
+ * Mount the scene to a canvas element or CSS selector.
118
+ */
119
+ mount(canvas: HTMLCanvasElement | string, options?: CanvasRenderingContext2DSettings): this {
120
+ if (typeof canvas === 'string') {
121
+ const el = document.querySelector<HTMLCanvasElement>(canvas);
122
+
123
+ if (!el) {
124
+ throw new Error(`Scene.mount(): no element found for selector "${canvas}".`);
125
+ }
126
+
127
+ canvas = el;
128
+ }
129
+
130
+ this.#runner?.destroy();
131
+ this.#runner = new SceneCanvas(canvas, this.#layers, this.#frameRate, options ?? this.#defaultOptions);
132
+ return this;
133
+ }
134
+
135
+ /**
136
+ * Add an effect layer. Layers are rendered in the order they are added.
137
+ * If the scene is already running, the layer is mounted immediately.
138
+ */
139
+ layer(effect: SimulationLayer): this {
140
+ this.#layers.push(effect);
141
+
142
+ if (this.#runner?.isTicking) {
143
+ effect.onMount(this.#runner.canvas);
144
+ }
145
+
146
+ return this;
147
+ }
148
+
149
+ /**
150
+ * Start the render loop.
151
+ */
152
+ start(): this {
153
+ this.#runner?.start();
154
+ return this;
155
+ }
156
+
157
+ /**
158
+ * Pause rendering without destroying state. Use resume() to continue.
159
+ */
160
+ pause(): this {
161
+ this.#runner?.pause();
162
+ return this;
163
+ }
164
+
165
+ /**
166
+ * Resume rendering after pause().
167
+ */
168
+ resume(): this {
169
+ this.#runner?.resume();
170
+ return this;
171
+ }
172
+
173
+ /**
174
+ * Stop and destroy all layers.
175
+ */
176
+ destroy(): void {
177
+ this.#runner?.destroy();
178
+ this.#runner = null;
179
+ }
180
+
181
+ get speed(): number {
182
+ return this.#runner?.speed ?? 1;
183
+ }
184
+
185
+ set speed(value: number) {
186
+ if (this.#runner) {
187
+ this.#runner.speed = value;
188
+ }
189
+ }
190
+
191
+ get isTicking(): boolean {
192
+ return this.#runner?.isTicking ?? false;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Factory alternative to `new Scene()`. Call .mount() and .layer() on the returned instance.
198
+ */
199
+ export function createScene(frameRate?: number, options?: CanvasRenderingContext2DSettings): Scene {
200
+ return new Scene(frameRate, options);
201
+ }
@@ -0,0 +1,3 @@
1
+ export { ShootingStarSystem } from './system';
2
+ export type { ShootingStarSystemConfig } from './system';
3
+ export type { ShootingStar } from './types';
@@ -0,0 +1,151 @@
1
+ import type { ShootingStar } from './types';
2
+
3
+ export interface ShootingStarSystemConfig {
4
+ readonly interval: [number, number];
5
+ readonly color?: [number, number, number];
6
+ readonly trailLength?: number;
7
+ readonly trailAlphaFactor?: number;
8
+ readonly speed?: number;
9
+ readonly scale?: number;
10
+ readonly alphaMin?: number;
11
+ readonly alphaRange?: number;
12
+ readonly decayMin?: number;
13
+ readonly decayRange?: number;
14
+ }
15
+
16
+ export class ShootingStarSystem {
17
+ readonly #interval: [number, number];
18
+ readonly #color: [number, number, number];
19
+ readonly #trailLength: number;
20
+ readonly #trailAlphaFactor: number;
21
+ readonly #speed: number;
22
+ readonly #scale: number;
23
+ readonly #alphaMin: number;
24
+ readonly #alphaRange: number;
25
+ readonly #decayMin: number;
26
+ readonly #decayRange: number;
27
+ readonly #rng: () => number;
28
+ #cooldown: number;
29
+ #stars: ShootingStar[] = [];
30
+
31
+ constructor(config: ShootingStarSystemConfig, rng: () => number) {
32
+ this.#interval = config.interval;
33
+ this.#color = config.color ?? [200, 230, 255];
34
+ this.#trailLength = config.trailLength ?? 18;
35
+ this.#trailAlphaFactor = config.trailAlphaFactor ?? 0.5;
36
+ this.#speed = config.speed ?? 1;
37
+ this.#scale = config.scale ?? 1;
38
+ this.#alphaMin = config.alphaMin ?? 0.7;
39
+ this.#alphaRange = config.alphaRange ?? 0.3;
40
+ this.#decayMin = config.decayMin ?? 0.008;
41
+ this.#decayRange = config.decayRange ?? 0.01;
42
+ this.#rng = rng;
43
+ this.#cooldown = this.#interval[0] + this.#rng() * (this.#interval[1] - this.#interval[0]);
44
+ }
45
+
46
+ tick(dt: number, width: number, height: number): void {
47
+ this.#cooldown -= dt;
48
+
49
+ if (this.#cooldown <= 0) {
50
+ this.#stars.push(this.#create(width, height));
51
+ this.#cooldown = this.#interval[0] + this.#rng() * (this.#interval[1] - this.#interval[0]);
52
+ }
53
+
54
+ let alive = 0;
55
+
56
+ for (let i = 0; i < this.#stars.length; i++) {
57
+ const star = this.#stars[i];
58
+
59
+ const trail = star.trail;
60
+ const maxLen = this.#trailLength;
61
+
62
+ if (trail.length < maxLen) {
63
+ trail.push({x: star.x, y: star.y});
64
+ star.trailHead = trail.length - 1;
65
+ } else {
66
+ const next = (star.trailHead + 1) % maxLen;
67
+ trail[next].x = star.x;
68
+ trail[next].y = star.y;
69
+ star.trailHead = next;
70
+ }
71
+
72
+ star.x += star.vx * this.#speed * dt;
73
+ star.y += star.vy * this.#speed * dt;
74
+ star.alpha -= star.decay * dt;
75
+
76
+ const inBounds = star.alpha > 0 && star.x > -50 && star.x < width + 50 && star.y < height + 50;
77
+
78
+ if (inBounds) {
79
+ this.#stars[alive++] = star;
80
+ }
81
+ }
82
+
83
+ this.#stars.length = alive;
84
+ }
85
+
86
+ draw(ctx: CanvasRenderingContext2D): void {
87
+ const [cr, cg, cb] = this.#color;
88
+
89
+ ctx.globalCompositeOperation = 'lighter';
90
+
91
+ for (const star of this.#stars) {
92
+ const trail = star.trail;
93
+ const trailLen = trail.length;
94
+ const isFull = trailLen === this.#trailLength;
95
+ const oldest = isFull ? (star.trailHead + 1) % trailLen : 0;
96
+
97
+ for (let t = 0; t < trailLen; t++) {
98
+ const progress = t / trailLen;
99
+ const trailAlpha = star.alpha * progress * this.#trailAlphaFactor;
100
+ const trailSize = star.size * progress * this.#scale;
101
+
102
+ if (trailAlpha < 0.01) {
103
+ continue;
104
+ }
105
+
106
+ const idx = (oldest + t) % trailLen;
107
+
108
+ ctx.globalAlpha = trailAlpha;
109
+ ctx.beginPath();
110
+ ctx.arc(trail[idx].x, trail[idx].y, trailSize, 0, Math.PI * 2);
111
+ ctx.fillStyle = `rgb(${cr}, ${cg}, ${cb})`;
112
+ ctx.fill();
113
+ }
114
+
115
+ const alpha = star.alpha;
116
+ const headSize = star.size * 2 * this.#scale;
117
+ const glow = ctx.createRadialGradient(star.x, star.y, 0, star.x, star.y, headSize);
118
+ glow.addColorStop(0, `rgba(${cr}, ${cg}, ${cb}, ${alpha})`);
119
+ glow.addColorStop(0.5, `rgba(${cr}, ${cg}, ${cb}, ${alpha * 0.3})`);
120
+ glow.addColorStop(1, `rgba(${cr}, ${cg}, ${cb}, 0)`);
121
+
122
+ ctx.globalAlpha = 1;
123
+ ctx.beginPath();
124
+ ctx.arc(star.x, star.y, headSize, 0, Math.PI * 2);
125
+ ctx.fillStyle = glow;
126
+ ctx.fill();
127
+ }
128
+
129
+ ctx.globalCompositeOperation = 'source-over';
130
+ ctx.globalAlpha = 1;
131
+ }
132
+
133
+ #create(width: number, height: number): ShootingStar {
134
+ const startX = this.#rng() * width * 0.8;
135
+ const startY = this.#rng() * height * 0.4;
136
+ const angle = 0.3 + this.#rng() * 0.5;
137
+ const speed = 8 + this.#rng() * 12;
138
+
139
+ return {
140
+ x: startX,
141
+ y: startY,
142
+ vx: Math.cos(angle) * speed * this.#scale,
143
+ vy: Math.sin(angle) * speed * this.#scale,
144
+ alpha: this.#alphaMin + this.#rng() * this.#alphaRange,
145
+ size: 1.5 + this.#rng() * 2,
146
+ decay: this.#decayMin + this.#rng() * this.#decayRange,
147
+ trail: [],
148
+ trailHead: 0
149
+ };
150
+ }
151
+ }
@@ -0,0 +1,11 @@
1
+ export type ShootingStar = {
2
+ x: number;
3
+ y: number;
4
+ vx: number;
5
+ vy: number;
6
+ alpha: number;
7
+ size: number;
8
+ decay: number;
9
+ trail: { x: number; y: number }[];
10
+ trailHead: number;
11
+ };
@@ -0,0 +1,83 @@
1
+ import { LimitedFrameRateCanvas } from './canvas';
2
+ import { applyEdgeFade } from './fade';
3
+ import type { EdgeFade, SimulationLayer } from './layer';
4
+
5
+ export class SimulationCanvas extends LimitedFrameRateCanvas {
6
+ readonly #simulation: SimulationLayer;
7
+ readonly #contextOptions: CanvasRenderingContext2DSettings;
8
+ #offscreen: HTMLCanvasElement | null = null;
9
+ #offscreenCtx: CanvasRenderingContext2D | null = null;
10
+
11
+ constructor(canvas: HTMLCanvasElement, simulation: SimulationLayer, frameRate: number = 60, options: CanvasRenderingContext2DSettings = {colorSpace: 'display-p3'}) {
12
+ super(canvas, frameRate, options);
13
+ this.#simulation = simulation;
14
+ this.#contextOptions = options;
15
+
16
+ canvas.style.position = 'absolute';
17
+ canvas.style.top = '0';
18
+ canvas.style.left = '0';
19
+ canvas.style.height = '100%';
20
+ canvas.style.width = '100%';
21
+ }
22
+
23
+ withFade(fade: EdgeFade): this {
24
+ this.#simulation.fade = fade;
25
+ return this;
26
+ }
27
+
28
+ start(): void {
29
+ this.#simulation.onMount(this.canvas);
30
+ super.start();
31
+ }
32
+
33
+ destroy(): void {
34
+ this.#simulation.onUnmount(this.canvas);
35
+ super.destroy();
36
+ }
37
+
38
+ draw(): void {
39
+ this.canvas.height = this.height;
40
+ this.canvas.width = this.width;
41
+
42
+ const ctx = this.context;
43
+
44
+ if (this.#simulation.fade) {
45
+ const offCtx = this.#getOffscreenCtx(this.width, this.height);
46
+ offCtx.clearRect(0, 0, this.width, this.height);
47
+ this.#simulation.draw(offCtx, this.width, this.height);
48
+ applyEdgeFade(offCtx, this.width, this.height, this.#simulation.fade);
49
+ ctx.drawImage(this.#offscreen!, 0, 0);
50
+ } else {
51
+ ctx.save();
52
+ this.#simulation.draw(ctx, this.width, this.height);
53
+ ctx.restore();
54
+ }
55
+ }
56
+
57
+ tick(): void {
58
+ const dt = (this.delta > 0 && this.delta < 200 ? this.delta / (1000 / 60) : 1) * this.speed * LimitedFrameRateCanvas.globalSpeed;
59
+ this.#simulation.tick(dt, this.width, this.height);
60
+ }
61
+
62
+ onResize(): void {
63
+ super.onResize();
64
+
65
+ if (this.#offscreen) {
66
+ this.#offscreen.width = this.width;
67
+ this.#offscreen.height = this.height;
68
+ }
69
+
70
+ this.#simulation.onResize(this.width, this.height);
71
+ }
72
+
73
+ #getOffscreenCtx(width: number, height: number): CanvasRenderingContext2D {
74
+ if (!this.#offscreen) {
75
+ this.#offscreen = document.createElement('canvas');
76
+ this.#offscreen.width = width;
77
+ this.#offscreen.height = height;
78
+ this.#offscreenCtx = this.#offscreen.getContext('2d', this.#contextOptions)!;
79
+ }
80
+
81
+ return this.#offscreenCtx!;
82
+ }
83
+ }
@@ -1,3 +1,3 @@
1
- import { mulberry32 } from '@basmilius/utils';
1
+ import { type Mulberry32, mulberry32 } from '@basmilius/utils';
2
2
 
3
- export const MULBERRY = mulberry32(13);
3
+ export const MULBERRY: Mulberry32 = mulberry32(13);
package/src/snow/index.ts CHANGED
@@ -1,2 +1,9 @@
1
- export { SnowSimulation } from './simulation';
2
- export type { SnowSimulationConfig } from './simulation';
1
+ import { Snow } from './layer';
2
+ import type { SnowConfig } from './layer';
3
+ import type { Effect } from '../effect';
4
+
5
+ export function createSnow(config?: SnowConfig): Effect<SnowConfig> {
6
+ return new Snow(config);
7
+ }
8
+
9
+ export type { SnowConfig };