@basmilius/sparkle 2.1.0 → 2.3.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 (108) hide show
  1. package/dist/index.d.mts +317 -459
  2. package/dist/index.d.mts.map +1 -1
  3. package/dist/index.mjs +1258 -949
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +6 -2
  6. package/src/aurora/index.ts +9 -3
  7. package/src/aurora/layer.ts +57 -29
  8. package/src/balloons/index.ts +9 -3
  9. package/src/balloons/layer.ts +50 -19
  10. package/src/bubbles/index.ts +9 -3
  11. package/src/bubbles/layer.ts +30 -17
  12. package/src/canvas.ts +92 -2
  13. package/src/color.ts +11 -2
  14. package/src/confetti/index.ts +15 -3
  15. package/src/confetti/layer.ts +8 -5
  16. package/src/confetti/particle.ts +12 -11
  17. package/src/confetti/shapes.ts +84 -97
  18. package/src/donuts/consts.ts +2 -2
  19. package/src/donuts/index.ts +9 -3
  20. package/src/donuts/layer.ts +43 -12
  21. package/src/effect.ts +107 -0
  22. package/src/fade.ts +87 -0
  23. package/src/fireflies/index.ts +9 -3
  24. package/src/fireflies/layer.ts +26 -9
  25. package/src/fireflies/particle.ts +2 -2
  26. package/src/firepit/index.ts +9 -3
  27. package/src/firepit/layer.ts +26 -7
  28. package/src/fireworks/create-explosion.ts +237 -0
  29. package/src/fireworks/explosion.ts +1 -1
  30. package/src/fireworks/index.ts +15 -3
  31. package/src/fireworks/layer.ts +55 -304
  32. package/src/fireworks/spark.ts +2 -2
  33. package/src/fireworks/types.ts +2 -2
  34. package/src/glitter/index.ts +9 -4
  35. package/src/glitter/layer.ts +15 -7
  36. package/src/glitter/types.ts +10 -0
  37. package/src/index.ts +3 -4
  38. package/src/lanterns/index.ts +9 -4
  39. package/src/lanterns/layer.ts +22 -10
  40. package/src/lanterns/types.ts +8 -0
  41. package/src/layer.ts +13 -11
  42. package/src/leaves/index.ts +9 -4
  43. package/src/leaves/layer.ts +21 -14
  44. package/src/leaves/types.ts +9 -0
  45. package/src/lightning/index.ts +9 -4
  46. package/src/lightning/layer.ts +4 -4
  47. package/src/lightning/system.ts +3 -3
  48. package/src/lightning/types.ts +10 -2
  49. package/src/matrix/index.ts +9 -4
  50. package/src/matrix/layer.ts +15 -7
  51. package/src/matrix/types.ts +9 -0
  52. package/src/orbits/index.ts +9 -4
  53. package/src/orbits/layer.ts +51 -21
  54. package/src/orbits/types.ts +12 -1
  55. package/src/particles/index.ts +9 -3
  56. package/src/particles/layer.ts +55 -12
  57. package/src/petals/index.ts +9 -3
  58. package/src/petals/layer.ts +29 -13
  59. package/src/plasma/index.ts +9 -3
  60. package/src/plasma/layer.ts +21 -6
  61. package/src/rain/index.ts +9 -3
  62. package/src/rain/layer.ts +30 -8
  63. package/src/sandstorm/index.ts +9 -3
  64. package/src/sandstorm/layer.ts +26 -9
  65. package/src/scene.ts +204 -0
  66. package/src/shooting-stars/system.ts +26 -24
  67. package/src/shooting-stars/types.ts +2 -1
  68. package/src/simulation-canvas.ts +45 -6
  69. package/src/snow/index.ts +9 -3
  70. package/src/snow/layer.ts +24 -11
  71. package/src/sparklers/index.ts +13 -3
  72. package/src/sparklers/layer.ts +61 -15
  73. package/src/stars/index.ts +9 -3
  74. package/src/stars/layer.ts +28 -22
  75. package/src/streamers/index.ts +9 -3
  76. package/src/streamers/layer.ts +18 -6
  77. package/src/streamers/types.ts +1 -1
  78. package/src/waves/index.ts +9 -3
  79. package/src/waves/layer.ts +42 -45
  80. package/src/waves/types.ts +1 -0
  81. package/src/wormhole/index.ts +9 -3
  82. package/src/wormhole/layer.ts +22 -6
  83. package/src/aurora/simulation.ts +0 -19
  84. package/src/balloons/simulation.ts +0 -19
  85. package/src/bubbles/simulation.ts +0 -20
  86. package/src/confetti/simulation.ts +0 -27
  87. package/src/donuts/simulation.ts +0 -25
  88. package/src/fireflies/simulation.ts +0 -18
  89. package/src/firepit/simulation.ts +0 -17
  90. package/src/fireworks/simulation.ts +0 -18
  91. package/src/glitter/simulation.ts +0 -19
  92. package/src/lanterns/simulation.ts +0 -17
  93. package/src/layered.ts +0 -185
  94. package/src/leaves/simulation.ts +0 -18
  95. package/src/lightning/simulation.ts +0 -17
  96. package/src/matrix/simulation.ts +0 -18
  97. package/src/orbits/simulation.ts +0 -19
  98. package/src/particles/simulation.ts +0 -26
  99. package/src/petals/simulation.ts +0 -18
  100. package/src/plasma/simulation.ts +0 -17
  101. package/src/rain/simulation.ts +0 -21
  102. package/src/sandstorm/simulation.ts +0 -18
  103. package/src/snow/simulation.ts +0 -17
  104. package/src/sparklers/simulation.ts +0 -30
  105. package/src/stars/simulation.ts +0 -22
  106. package/src/streamers/simulation.ts +0 -16
  107. package/src/waves/simulation.ts +0 -18
  108. package/src/wormhole/simulation.ts +0 -17
@@ -1,4 +1,10 @@
1
- export { FirepitLayer } from './layer';
2
- export { FirepitSimulation } from './simulation';
3
- export type { FirepitSimulationConfig } from './simulation';
1
+ import { Firepit } from './layer';
2
+ import type { FirepitConfig } from './layer';
3
+ import type { Effect } from '../effect';
4
+
5
+ export function createFirepit(config?: FirepitConfig): Effect<FirepitConfig> {
6
+ return new Firepit(config);
7
+ }
8
+
9
+ export type { FirepitConfig };
4
10
  export type { Ember, FlameLayer } from './types';
@@ -1,19 +1,26 @@
1
- import { SimulationLayer } from '../layer';
1
+ import { Effect } from '../effect';
2
2
  import { MULBERRY } from './consts';
3
- import type { FirepitSimulationConfig } from './simulation';
4
3
  import type { Ember, FlameLayer } from './types';
5
4
 
6
- export class FirepitLayer extends SimulationLayer {
5
+ export interface FirepitConfig {
6
+ readonly embers?: number;
7
+ readonly flameWidth?: number;
8
+ readonly flameHeight?: number;
9
+ readonly intensity?: number;
10
+ readonly scale?: number;
11
+ }
12
+
13
+ export class Firepit extends Effect<FirepitConfig> {
7
14
  readonly #scale: number;
8
- readonly #flameWidth: number;
9
- readonly #flameHeight: number;
10
- readonly #intensity: number;
15
+ #flameWidth: number;
16
+ #flameHeight: number;
17
+ #intensity: number;
11
18
  #maxEmbers: number;
12
19
  #time: number = 0;
13
20
  #embers: Ember[] = [];
14
21
  #flameLayers: FlameLayer[] = [];
15
22
 
16
- constructor(config: FirepitSimulationConfig = {}) {
23
+ constructor(config: FirepitConfig = {}) {
17
24
  super();
18
25
 
19
26
  this.#scale = config.scale ?? 1;
@@ -38,6 +45,18 @@ export class FirepitLayer extends SimulationLayer {
38
45
  }
39
46
  }
40
47
 
48
+ configure(config: Partial<FirepitConfig>): void {
49
+ if (config.intensity !== undefined) {
50
+ this.#intensity = config.intensity;
51
+ }
52
+ if (config.flameWidth !== undefined) {
53
+ this.#flameWidth = config.flameWidth;
54
+ }
55
+ if (config.flameHeight !== undefined) {
56
+ this.#flameHeight = config.flameHeight;
57
+ }
58
+ }
59
+
41
60
  tick(dt: number, _width: number, _height: number): void {
42
61
  this.#time += 0.03 * dt * this.#intensity;
43
62
 
@@ -0,0 +1,237 @@
1
+ import type { Point } from '../point';
2
+ import { Explosion } from './explosion';
3
+ import { EXPLOSION_CONFIGS, type ExplosionType, type FireworkVariant } from './types';
4
+
5
+ function between(rng: () => number, min: number, max: number): number {
6
+ return min + rng() * (max - min);
7
+ }
8
+
9
+ /**
10
+ * Creates an array of {@link Explosion} particles for the given firework variant.
11
+ * Use this to fire a fully formed explosion burst in your own render loop without
12
+ * needing a {@link Fireworks} instance.
13
+ *
14
+ * @param variant - The firework variant to create.
15
+ * @param position - The center position of the explosion in canvas pixels.
16
+ * @param hue - Base hue in degrees (0–360).
17
+ * @param options - Optional overrides for `lineWidth` (default `5`) and `scale` (default `1`).
18
+ * @param rng - RNG function returning values in [0, 1). Defaults to `Math.random`.
19
+ */
20
+ export function createExplosion(
21
+ variant: FireworkVariant,
22
+ position: Point,
23
+ hue: number,
24
+ options: { lineWidth?: number; scale?: number } = {},
25
+ rng: () => number = Math.random
26
+ ): Explosion[] {
27
+ const lineWidth = options.lineWidth ?? 5;
28
+ const scale = options.scale ?? 1;
29
+ const explosions: Explosion[] = [];
30
+
31
+ switch (variant) {
32
+ case 'saturn':
33
+ createSaturn(explosions, position, hue, lineWidth, scale, rng);
34
+ break;
35
+ case 'dahlia':
36
+ createDahlia(explosions, position, hue, lineWidth, scale, rng);
37
+ break;
38
+ case 'heart':
39
+ createHeart(explosions, position, hue, lineWidth, scale, rng);
40
+ break;
41
+ case 'spiral':
42
+ createSpiral(explosions, position, hue, lineWidth, scale, rng);
43
+ break;
44
+ case 'flower':
45
+ createFlower(explosions, position, hue, lineWidth, scale, rng);
46
+ break;
47
+ case 'concentric':
48
+ createConcentric(explosions, position, hue, lineWidth, scale, rng);
49
+ break;
50
+ default: {
51
+ const type: ExplosionType = variant;
52
+ const config = EXPLOSION_CONFIGS[type];
53
+ const count = Math.floor(between(rng, config.particleCount[0], config.particleCount[1]));
54
+ const effectiveHue = type === 'brocade' ? between(rng, 35, 50) : hue;
55
+
56
+ for (let i = 0; i < count; i++) {
57
+ let angle: number | undefined;
58
+ let speed: number | undefined;
59
+
60
+ if (type === 'ring') {
61
+ angle = (i / count) * Math.PI * 2;
62
+ speed = between(rng, config.speed[0], config.speed[1]) * 0.5 + config.speed[0] * 0.5;
63
+ } else if (type === 'palm' || type === 'horsetail') {
64
+ const spread = type === 'horsetail' ? Math.PI / 8 : Math.PI / 5;
65
+ angle = -Math.PI / 2 + between(rng, -spread, spread);
66
+ }
67
+
68
+ explosions.push(new Explosion(position, effectiveHue, lineWidth, type, scale, angle, speed));
69
+ }
70
+ }
71
+ }
72
+
73
+ return explosions;
74
+ }
75
+
76
+ function createSaturn(explosions: Explosion[], position: Point, hue: number, lineWidth: number, scale: number, rng: () => number): void {
77
+ const velocity = between(rng, 4, 6);
78
+ const shellCount = Math.floor(between(rng, 25, 35));
79
+
80
+ for (let i = 0; i < shellCount; i++) {
81
+ const rad = (i / shellCount) * Math.PI * 2;
82
+
83
+ explosions.push(new Explosion(
84
+ position, hue, lineWidth, 'peony', scale,
85
+ rad + between(rng, -0.05, 0.05),
86
+ velocity + between(rng, -0.25, 0.25)
87
+ ));
88
+ }
89
+
90
+ const fillCount = Math.floor(between(rng, 40, 60));
91
+
92
+ for (let i = 0; i < fillCount; i++) {
93
+ explosions.push(new Explosion(
94
+ position, hue, lineWidth, 'peony', scale,
95
+ between(rng, 0, Math.PI * 2),
96
+ velocity * between(rng, 0, 1)
97
+ ));
98
+ }
99
+
100
+ const ringRotation = between(rng, 0, Math.PI * 2);
101
+ const ringCount = Math.floor(between(rng, 40, 55));
102
+ const ringVx = velocity * between(rng, 2, 3);
103
+ const ringVy = velocity * 0.6;
104
+
105
+ for (let i = 0; i < ringCount; i++) {
106
+ const rad = (i / ringCount) * Math.PI * 2;
107
+ const cx = Math.cos(rad) * ringVx + between(rng, -0.25, 0.25);
108
+ const cy = Math.sin(rad) * ringVy + between(rng, -0.25, 0.25);
109
+ const cosR = Math.cos(ringRotation);
110
+ const sinR = Math.sin(ringRotation);
111
+ const vx = cx * cosR - cy * sinR;
112
+ const vy = cx * sinR + cy * cosR;
113
+ const vz = Math.sin(rad) * velocity * 0.8;
114
+
115
+ explosions.push(new Explosion(
116
+ position, hue + 60, lineWidth, 'ring', scale,
117
+ Math.atan2(vy, vx),
118
+ Math.sqrt(vx * vx + vy * vy),
119
+ vz
120
+ ));
121
+ }
122
+ }
123
+
124
+ function createDahlia(explosions: Explosion[], position: Point, hue: number, lineWidth: number, scale: number, rng: () => number): void {
125
+ const petalCount = Math.floor(between(rng, 6, 9));
126
+ const particlesPerPetal = Math.floor(between(rng, 8, 12));
127
+
128
+ for (let petal = 0; petal < petalCount; petal++) {
129
+ const baseAngle = (petal / petalCount) * Math.PI * 2;
130
+ const petalHue = hue + (petal % 2 === 0 ? 25 : -25);
131
+
132
+ for (let i = 0; i < particlesPerPetal; i++) {
133
+ explosions.push(new Explosion(
134
+ position, petalHue, lineWidth, 'dahlia', scale,
135
+ baseAngle + between(rng, -0.3, 0.3)
136
+ ));
137
+ }
138
+ }
139
+ }
140
+
141
+ function createHeart(explosions: Explosion[], position: Point, hue: number, lineWidth: number, scale: number, rng: () => number): void {
142
+ const velocity = between(rng, 3, 5);
143
+ const count = Math.floor(between(rng, 60, 80));
144
+ const rotation = between(rng, -0.3, 0.3);
145
+ const cosR = Math.cos(rotation);
146
+ const sinR = Math.sin(rotation);
147
+
148
+ for (let i = 0; i < count; i++) {
149
+ const t = (i / count) * Math.PI * 2;
150
+ const hx = 16 * Math.pow(Math.sin(t), 3);
151
+ const hy = -(13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t));
152
+ const s = velocity / 16;
153
+ const vx = hx * s;
154
+ const vy = hy * s;
155
+ const rvx = vx * cosR - vy * sinR;
156
+ const rvy = vx * sinR + vy * cosR;
157
+
158
+ explosions.push(new Explosion(
159
+ position, hue, lineWidth, 'heart', scale,
160
+ Math.atan2(rvy, rvx),
161
+ Math.max(0.1, Math.sqrt(rvx * rvx + rvy * rvy) + between(rng, -0.15, 0.15))
162
+ ));
163
+ }
164
+ }
165
+
166
+ function createSpiral(explosions: Explosion[], position: Point, hue: number, lineWidth: number, scale: number, rng: () => number): void {
167
+ const arms = Math.floor(between(rng, 3, 5));
168
+ const particlesPerArm = Math.floor(between(rng, 15, 20));
169
+ const twist = between(rng, 2, 3.5);
170
+ const baseRotation = between(rng, 0, Math.PI * 2);
171
+
172
+ for (let arm = 0; arm < arms; arm++) {
173
+ const baseAngle = baseRotation + (arm / arms) * Math.PI * 2;
174
+ const armHue = hue + arm * (360 / arms / 3);
175
+
176
+ for (let i = 0; i < particlesPerArm; i++) {
177
+ const progress = i / particlesPerArm;
178
+
179
+ explosions.push(new Explosion(
180
+ position, armHue, lineWidth, 'spiral', scale,
181
+ baseAngle + progress * twist,
182
+ 2 + progress * 8 + between(rng, -0.3, 0.3)
183
+ ));
184
+ }
185
+ }
186
+ }
187
+
188
+ function createFlower(explosions: Explosion[], position: Point, hue: number, lineWidth: number, scale: number, rng: () => number): void {
189
+ const velocity = between(rng, 4, 7);
190
+ const count = Math.floor(between(rng, 70, 90));
191
+ const petals = Math.floor(between(rng, 2, 4));
192
+ const rotation = between(rng, 0, Math.PI * 2);
193
+
194
+ for (let i = 0; i < count; i++) {
195
+ const t = (i / count) * Math.PI * 2;
196
+ const r = Math.abs(Math.cos(petals * t));
197
+ const speed = velocity * r;
198
+
199
+ if (speed < 0.3) {
200
+ continue;
201
+ }
202
+
203
+ explosions.push(new Explosion(
204
+ position, hue + between(rng, -15, 15), lineWidth, 'flower', scale,
205
+ t + rotation,
206
+ speed + between(rng, -0.2, 0.2)
207
+ ));
208
+ }
209
+ }
210
+
211
+ function createConcentric(explosions: Explosion[], position: Point, hue: number, lineWidth: number, scale: number, rng: () => number): void {
212
+ const outerCount = Math.floor(between(rng, 35, 50));
213
+ const outerSpeed = between(rng, 7, 10);
214
+
215
+ for (let i = 0; i < outerCount; i++) {
216
+ const angle = (i / outerCount) * Math.PI * 2;
217
+
218
+ explosions.push(new Explosion(
219
+ position, hue, lineWidth, 'ring', scale,
220
+ angle + between(rng, -0.05, 0.05),
221
+ outerSpeed + between(rng, -0.25, 0.25)
222
+ ));
223
+ }
224
+
225
+ const innerCount = Math.floor(between(rng, 25, 35));
226
+ const innerSpeed = between(rng, 3, 5);
227
+
228
+ for (let i = 0; i < innerCount; i++) {
229
+ const angle = (i / innerCount) * Math.PI * 2;
230
+
231
+ explosions.push(new Explosion(
232
+ position, hue + 120, lineWidth, 'ring', scale,
233
+ angle + between(rng, -0.05, 0.05),
234
+ innerSpeed + between(rng, -0.25, 0.25)
235
+ ));
236
+ }
237
+ }
@@ -77,7 +77,7 @@ export class Explosion {
77
77
  return false;
78
78
  }
79
79
 
80
- if (this.#alpha <= this.#decay * 3) {
80
+ if (this.#alpha <= 0.4) {
81
81
  this.#hasCrackled = true;
82
82
  return true;
83
83
  }
@@ -1,7 +1,19 @@
1
+ import { Fireworks } from './layer';
2
+ import type { FireworksConfig, FireworkVariant } from './types';
3
+ import type { Point } from '../point';
4
+ import type { Effect } from '../effect';
5
+
6
+ export interface FireworksInstance extends Effect<FireworksConfig> {
7
+ launch(variant: FireworkVariant, position?: Point): void;
8
+ }
9
+
10
+ export function createFireworks(config?: FireworksConfig): FireworksInstance {
11
+ return new Fireworks(config) as FireworksInstance;
12
+ }
13
+
14
+ export { createExplosion } from './create-explosion';
1
15
  export { Explosion } from './explosion';
2
16
  export { Firework } from './firework';
3
- export { FireworkLayer } from './layer';
4
17
  export { Spark } from './spark';
5
- export * from './simulation';
6
18
  export { EXPLOSION_CONFIGS, FIREWORK_VARIANTS } from './types';
7
- export type { ExplosionConfig, ExplosionType, FireworkSimulationConfig, FireworkVariant, ParticleShape } from './types';
19
+ export type { ExplosionConfig, ExplosionType, FireworksConfig, FireworkVariant, ParticleShape } from './types';