@dopaminefx/effect-confetti 0.1.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/confetti-renderer.d.ts +66 -0
- package/dist/confetti-renderer.d.ts.map +1 -0
- package/dist/confetti-renderer.js +156 -0
- package/dist/confetti-renderer.js.map +1 -0
- package/dist/confetti-shader.d.ts +31 -0
- package/dist/confetti-shader.d.ts.map +1 -0
- package/dist/confetti-shader.js +108 -0
- package/dist/confetti-shader.js.map +1 -0
- package/dist/confetti-tempo.d.ts +13 -0
- package/dist/confetti-tempo.d.ts.map +1 -0
- package/dist/confetti-tempo.js +28 -0
- package/dist/confetti-tempo.js.map +1 -0
- package/dist/confetti.dope.json +456 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
- package/src/confetti-renderer.ts +193 -0
- package/src/confetti-shader.ts +116 -0
- package/src/confetti-tempo.ts +27 -0
- package/src/confetti.dope.json +456 -0
- package/src/index.ts +51 -0
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
{
|
|
2
|
+
"fmt": "dopamine-effect",
|
|
3
|
+
"v": "1.0.0",
|
|
4
|
+
"id": "dopamine.celebrate.confetti",
|
|
5
|
+
"meta": {
|
|
6
|
+
"name": "Confetti",
|
|
7
|
+
"description": "A burst of paper confetti launches upward from the action then tumbles down under gravity with air-drag flutter — spinning rectangles and petals in many OKLCH hues that sway, settle, and fade. The signature is the downward, physical, fluttering fall (deliberately distinct from Solarbloom's upward drifting motes).",
|
|
8
|
+
"tags": [
|
|
9
|
+
"celebrate",
|
|
10
|
+
"success",
|
|
11
|
+
"particles"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
"controls": {
|
|
15
|
+
"mood": {
|
|
16
|
+
"type": "enum",
|
|
17
|
+
"default": "celebratory",
|
|
18
|
+
"options": [
|
|
19
|
+
"serene",
|
|
20
|
+
"celebratory",
|
|
21
|
+
"electric"
|
|
22
|
+
],
|
|
23
|
+
"ui": "segmented"
|
|
24
|
+
},
|
|
25
|
+
"intensity": {
|
|
26
|
+
"type": "scalar",
|
|
27
|
+
"default": 0.75,
|
|
28
|
+
"min": 0,
|
|
29
|
+
"max": 1,
|
|
30
|
+
"step": 0.01,
|
|
31
|
+
"ui": "slider"
|
|
32
|
+
},
|
|
33
|
+
"whimsy": {
|
|
34
|
+
"type": "scalar",
|
|
35
|
+
"default": 0.5,
|
|
36
|
+
"min": 0,
|
|
37
|
+
"max": 1,
|
|
38
|
+
"step": 0.01,
|
|
39
|
+
"ui": "slider"
|
|
40
|
+
},
|
|
41
|
+
"seed": {
|
|
42
|
+
"type": "int",
|
|
43
|
+
"default": null,
|
|
44
|
+
"nullable": true
|
|
45
|
+
},
|
|
46
|
+
"origin": {
|
|
47
|
+
"type": "point",
|
|
48
|
+
"default": "center"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"baselines": {
|
|
52
|
+
"serene": {
|
|
53
|
+
"durationMs": 3000,
|
|
54
|
+
"lightness": 0.86,
|
|
55
|
+
"chroma": 0.07,
|
|
56
|
+
"hueCenter": 210,
|
|
57
|
+
"hueRange": 150,
|
|
58
|
+
"pieceCount": 34,
|
|
59
|
+
"spread": 0.42,
|
|
60
|
+
"launchSpeed": 0.78,
|
|
61
|
+
"gravity": 0.62,
|
|
62
|
+
"flutter": 0.55,
|
|
63
|
+
"pieceSize": 1.12,
|
|
64
|
+
"spin": 0.7
|
|
65
|
+
},
|
|
66
|
+
"celebratory": {
|
|
67
|
+
"durationMs": 2300,
|
|
68
|
+
"lightness": 0.82,
|
|
69
|
+
"chroma": 0.16,
|
|
70
|
+
"hueCenter": 45,
|
|
71
|
+
"hueRange": 340,
|
|
72
|
+
"pieceCount": 70,
|
|
73
|
+
"spread": 0.62,
|
|
74
|
+
"launchSpeed": 1,
|
|
75
|
+
"gravity": 0.9,
|
|
76
|
+
"flutter": 0.85,
|
|
77
|
+
"pieceSize": 1,
|
|
78
|
+
"spin": 1
|
|
79
|
+
},
|
|
80
|
+
"electric": {
|
|
81
|
+
"durationMs": 1900,
|
|
82
|
+
"lightness": 0.8,
|
|
83
|
+
"chroma": 0.23,
|
|
84
|
+
"hueCenter": 320,
|
|
85
|
+
"hueRange": 300,
|
|
86
|
+
"pieceCount": 100,
|
|
87
|
+
"spread": 0.82,
|
|
88
|
+
"launchSpeed": 1.28,
|
|
89
|
+
"gravity": 1.05,
|
|
90
|
+
"flutter": 1.15,
|
|
91
|
+
"pieceSize": 0.9,
|
|
92
|
+
"spin": 1.35
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"palette": {
|
|
96
|
+
"model": "oklch",
|
|
97
|
+
"space": "linear-srgb",
|
|
98
|
+
"generator": "golden-angle",
|
|
99
|
+
"goldenAngleDeg": 137.50776405003785,
|
|
100
|
+
"stops": 3,
|
|
101
|
+
"hueSpread": 0.85,
|
|
102
|
+
"lightness": {
|
|
103
|
+
"baseline": "lightness",
|
|
104
|
+
"perStop": [
|
|
105
|
+
0,
|
|
106
|
+
0.05,
|
|
107
|
+
-0.06
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
"chroma": {
|
|
111
|
+
"from": {
|
|
112
|
+
"mul": [
|
|
113
|
+
{
|
|
114
|
+
"baseline": "chroma"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"lerp": [
|
|
118
|
+
"intensity",
|
|
119
|
+
0.7,
|
|
120
|
+
1.6
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
"perStop": [
|
|
126
|
+
0,
|
|
127
|
+
0.03,
|
|
128
|
+
-0.01
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
"seed": {
|
|
132
|
+
"deterministic": true,
|
|
133
|
+
"source": "controls.seed",
|
|
134
|
+
"prng": "mulberry32"
|
|
135
|
+
},
|
|
136
|
+
"perMood": {
|
|
137
|
+
"serene": {
|
|
138
|
+
"hueCenter": 210,
|
|
139
|
+
"hueRange": 150,
|
|
140
|
+
"lightness": 0.86,
|
|
141
|
+
"chroma": 0.07
|
|
142
|
+
},
|
|
143
|
+
"celebratory": {
|
|
144
|
+
"hueCenter": 45,
|
|
145
|
+
"hueRange": 340,
|
|
146
|
+
"lightness": 0.82,
|
|
147
|
+
"chroma": 0.16
|
|
148
|
+
},
|
|
149
|
+
"electric": {
|
|
150
|
+
"hueCenter": 320,
|
|
151
|
+
"hueRange": 300,
|
|
152
|
+
"lightness": 0.8,
|
|
153
|
+
"chroma": 0.23
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"tempo": {
|
|
158
|
+
"durationMs": {
|
|
159
|
+
"from": {
|
|
160
|
+
"round": {
|
|
161
|
+
"mul": [
|
|
162
|
+
{
|
|
163
|
+
"baseline": "durationMs"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"lerp": [
|
|
167
|
+
"intensity",
|
|
168
|
+
1.15,
|
|
169
|
+
0.85
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
"frame": {
|
|
177
|
+
"amp": {
|
|
178
|
+
"lt": [
|
|
179
|
+
0,
|
|
180
|
+
{
|
|
181
|
+
"input": "life"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"lt": [
|
|
185
|
+
{
|
|
186
|
+
"input": "life"
|
|
187
|
+
},
|
|
188
|
+
1,
|
|
189
|
+
{
|
|
190
|
+
"lt": [
|
|
191
|
+
{
|
|
192
|
+
"input": "life"
|
|
193
|
+
},
|
|
194
|
+
0.12,
|
|
195
|
+
{
|
|
196
|
+
"easeOutBack": [
|
|
197
|
+
{
|
|
198
|
+
"div": [
|
|
199
|
+
{
|
|
200
|
+
"input": "life"
|
|
201
|
+
},
|
|
202
|
+
0.12
|
|
203
|
+
]
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"param": "overshoot"
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"lt": [
|
|
212
|
+
{
|
|
213
|
+
"input": "life"
|
|
214
|
+
},
|
|
215
|
+
0.7,
|
|
216
|
+
1,
|
|
217
|
+
{
|
|
218
|
+
"sub": [
|
|
219
|
+
1,
|
|
220
|
+
{
|
|
221
|
+
"mul": [
|
|
222
|
+
{
|
|
223
|
+
"easeOutCubic": {
|
|
224
|
+
"div": [
|
|
225
|
+
{
|
|
226
|
+
"sub": [
|
|
227
|
+
{
|
|
228
|
+
"input": "life"
|
|
229
|
+
},
|
|
230
|
+
0.7
|
|
231
|
+
]
|
|
232
|
+
},
|
|
233
|
+
0.3
|
|
234
|
+
]
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
0.85
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
]
|
|
245
|
+
},
|
|
246
|
+
0
|
|
247
|
+
]
|
|
248
|
+
},
|
|
249
|
+
0
|
|
250
|
+
]
|
|
251
|
+
},
|
|
252
|
+
"extras": {}
|
|
253
|
+
},
|
|
254
|
+
"reducedMotion": {
|
|
255
|
+
"peakMs": 320,
|
|
256
|
+
"holdMs": 420
|
|
257
|
+
},
|
|
258
|
+
"note": "Datafied per-frame logic (the web/Swift/Kotlin confettiAmp envelope, delta-0). amp = the launch-then-fall brightness: a sharp easeOutBack POP over the first 12% of life (overshoot at launch), a luminous full sustain to 70%, then a soft easeOutCubic fade over the last 30% as the last pieces settle (per-piece dimming rides the panel draw). The pieces' poses (ballistic arc + flutter sway + spin) are panel GEOMETRY — code by design (the per-platform ConfettiPanel draw)."
|
|
259
|
+
},
|
|
260
|
+
"geometry": {
|
|
261
|
+
"kind": "radial",
|
|
262
|
+
"viewBox": [
|
|
263
|
+
0,
|
|
264
|
+
0,
|
|
265
|
+
100,
|
|
266
|
+
100
|
|
267
|
+
],
|
|
268
|
+
"outlines": {
|
|
269
|
+
"piece": {
|
|
270
|
+
"role": "confetti-piece",
|
|
271
|
+
"note": "Procedural paper pieces (rounded rectangles + a few petals) traced in the panel from pieceCount + pieceSeed; rotated by the per-piece spin."
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
"render": {
|
|
276
|
+
"params": {
|
|
277
|
+
"exposure": {
|
|
278
|
+
"type": "float",
|
|
279
|
+
"from": {
|
|
280
|
+
"lerp": [
|
|
281
|
+
"intensity",
|
|
282
|
+
0.95,
|
|
283
|
+
1.45
|
|
284
|
+
]
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
"pieceCount": {
|
|
288
|
+
"type": "float",
|
|
289
|
+
"from": {
|
|
290
|
+
"round": {
|
|
291
|
+
"mul": [
|
|
292
|
+
{
|
|
293
|
+
"baseline": "pieceCount"
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
"lerp": [
|
|
297
|
+
"intensity",
|
|
298
|
+
0.45,
|
|
299
|
+
1
|
|
300
|
+
]
|
|
301
|
+
}
|
|
302
|
+
]
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
"clampMax": "MAX_PIECES"
|
|
306
|
+
},
|
|
307
|
+
"spread": {
|
|
308
|
+
"type": "float",
|
|
309
|
+
"from": {
|
|
310
|
+
"mul": [
|
|
311
|
+
{
|
|
312
|
+
"baseline": "spread"
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"lerp": [
|
|
316
|
+
"intensity",
|
|
317
|
+
0.75,
|
|
318
|
+
1.15
|
|
319
|
+
]
|
|
320
|
+
}
|
|
321
|
+
]
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
"launchSpeed": {
|
|
325
|
+
"type": "float",
|
|
326
|
+
"from": {
|
|
327
|
+
"mul": [
|
|
328
|
+
{
|
|
329
|
+
"baseline": "launchSpeed"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
"lerp": [
|
|
333
|
+
"intensity",
|
|
334
|
+
0.8,
|
|
335
|
+
1.3
|
|
336
|
+
]
|
|
337
|
+
}
|
|
338
|
+
]
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
"gravity": {
|
|
342
|
+
"type": "float",
|
|
343
|
+
"from": {
|
|
344
|
+
"baseline": "gravity"
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
"flutter": {
|
|
348
|
+
"type": "float",
|
|
349
|
+
"from": {
|
|
350
|
+
"mul": [
|
|
351
|
+
{
|
|
352
|
+
"baseline": "flutter"
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
"lerp": [
|
|
356
|
+
"intensity",
|
|
357
|
+
0.85,
|
|
358
|
+
1.15
|
|
359
|
+
]
|
|
360
|
+
}
|
|
361
|
+
]
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
"pieceSize": {
|
|
365
|
+
"type": "float",
|
|
366
|
+
"from": {
|
|
367
|
+
"mul": [
|
|
368
|
+
{
|
|
369
|
+
"baseline": "pieceSize"
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
"lerp": [
|
|
373
|
+
"intensity",
|
|
374
|
+
1.1,
|
|
375
|
+
0.85
|
|
376
|
+
]
|
|
377
|
+
}
|
|
378
|
+
]
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
"spin": {
|
|
382
|
+
"type": "float",
|
|
383
|
+
"from": {
|
|
384
|
+
"baseline": "spin"
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
"overshoot": {
|
|
388
|
+
"type": "float",
|
|
389
|
+
"from": {
|
|
390
|
+
"lerp": [
|
|
391
|
+
"intensity",
|
|
392
|
+
0.6,
|
|
393
|
+
1.2
|
|
394
|
+
]
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
"style": {
|
|
398
|
+
"type": "float",
|
|
399
|
+
"from": {
|
|
400
|
+
"control": "whimsy"
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
"shadowHeightFrac": 0.5,
|
|
405
|
+
"panel": {
|
|
406
|
+
"sampler": "uPanel",
|
|
407
|
+
"texture": 0,
|
|
408
|
+
"note": "The Canvas2D/CoreGraphics/android.graphics panel (the spinning paper pieces rasterized once per piece per frame, RGB = Σ per-piece lit colour × fade) the host redraws every frame; the draw itself — the ballistic launch-then-fall poses + per-piece paper/cel shading — is the per-platform panel-draw seam (web panelDraw hook / ConfettiPanel files). The shader samples it and applies the global gain (amp · exposure), ACES tonemap, the cel posterize toward the whimsy end, an ordered dither, and the soft cast shadow."
|
|
409
|
+
},
|
|
410
|
+
"consts": {
|
|
411
|
+
"MAX_PIECES": 120
|
|
412
|
+
},
|
|
413
|
+
"config": {
|
|
414
|
+
"usesOrigin": true,
|
|
415
|
+
"stepping": "none"
|
|
416
|
+
},
|
|
417
|
+
"backends": {
|
|
418
|
+
"webgl2": {
|
|
419
|
+
"stage": "fullscreen-triangle",
|
|
420
|
+
"blend": "screen",
|
|
421
|
+
"shader": {
|
|
422
|
+
"program": "confetti"
|
|
423
|
+
},
|
|
424
|
+
"note": "Hybrid: a Canvas2D panel (the paper pieces) is uploaded as a texture; the shader adds the global gain, filmic tonemap, cel posterize, dither and the cast shadow."
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
"fallbackOrder": [
|
|
428
|
+
"webgl2"
|
|
429
|
+
]
|
|
430
|
+
},
|
|
431
|
+
"binding": {
|
|
432
|
+
"note": "CROSS-PLATFORM uniform-binding contract. Which render.params are NOT shader uniforms, the seed-keyed scatter field, the per-frame/host extras, and the texture samplers — one source of truth for the web u<Name> list, the Swift struct + packer, and the MSL struct. SHIPS in the portable .dope (the runtime derives its uniform bindings from it); the toolchain consumes it too for the Metal struct codegen. Only `x-build`, `slug` and `kind` stay toolchain-only.",
|
|
433
|
+
"excludeParams": [
|
|
434
|
+
"style",
|
|
435
|
+
"overshoot",
|
|
436
|
+
"durationMs",
|
|
437
|
+
"pieceCount",
|
|
438
|
+
"spread",
|
|
439
|
+
"launchSpeed",
|
|
440
|
+
"gravity",
|
|
441
|
+
"flutter",
|
|
442
|
+
"pieceSize",
|
|
443
|
+
"spin"
|
|
444
|
+
],
|
|
445
|
+
"scatterKey": "pieceSeed",
|
|
446
|
+
"extras": [],
|
|
447
|
+
"samplers": [
|
|
448
|
+
{
|
|
449
|
+
"web": "uPanel",
|
|
450
|
+
"name": "panelTex",
|
|
451
|
+
"texture": 0,
|
|
452
|
+
"note": "the per-frame panel texture (render.panel)"
|
|
453
|
+
}
|
|
454
|
+
]
|
|
455
|
+
}
|
|
456
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confetti (the celebration success effect) — the DATA-DRIVEN panel factory shim.
|
|
3
|
+
*
|
|
4
|
+
* The quintessential celebration: a burst of paper confetti POPS upward from the
|
|
5
|
+
* action then TUMBLES DOWN under gravity with air-drag flutter — spinning
|
|
6
|
+
* rectangles + petals in many OKLCH hues that sway, settle, and fade. The
|
|
7
|
+
* signature is the downward, physical, fluttering fall, deliberately distinct
|
|
8
|
+
* from Solarbloom's UPWARD drifting motes.
|
|
9
|
+
*
|
|
10
|
+
* Confetti is a PANEL HYBRID, converged onto the same declarative path as
|
|
11
|
+
* comic/heartburst: everything that isn't the shader or the Canvas2D draw is
|
|
12
|
+
* DATA in confetti.dope.json, interpreted by the shared backbone:
|
|
13
|
+
* - the mood→params mapping + the OKLCH golden-angle palette (the loader),
|
|
14
|
+
* - the per-frame launch-then-fall amplitude (`tempo.frame.amp`, delta-0 with
|
|
15
|
+
* the old `confettiAmp` hook),
|
|
16
|
+
* - the shadow height (`render.shadowHeightFrac`), the panel wiring
|
|
17
|
+
* (`render.panel`), the no-snap clock (`render.config.stepping: "none"`),
|
|
18
|
+
* the MAX_PIECES clamp const (`render.consts`), and `tempo.reducedMotion`,
|
|
19
|
+
* - the uniform `binding` contract (the `u<Name>` list + exceptions).
|
|
20
|
+
*
|
|
21
|
+
* The genuinely code-shaped parts that stay JS are the GLSL (confetti-shader.ts —
|
|
22
|
+
* the single source the MSL + Kotlin shaders are generated from) and the Canvas2D
|
|
23
|
+
* panel draw (confetti-renderer.ts — the ballistic per-piece poses).
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { CONFETTI_FRAGMENT_SRC, CONFETTI_VERTEX_SRC } from "./confetti-shader.js";
|
|
27
|
+
import { drawConfettiFrame, type ConfettiRenderParams } from "./confetti-renderer.js";
|
|
28
|
+
import { parseDope, registerDopePanelEffect, type EffectFactory } from "@dopaminefx/core";
|
|
29
|
+
import doc from "./confetti.dope.json";
|
|
30
|
+
|
|
31
|
+
export type { ConfettiRenderParams } from "./confetti-renderer.js";
|
|
32
|
+
|
|
33
|
+
// MAX_PIECES is the single source of truth for the panel loop bound + the integer
|
|
34
|
+
// clamp the `.dope` mapping references (`render.params.pieceCount.clampMax`,
|
|
35
|
+
// resolved from `render.consts.MAX_PIECES`). Re-exported for hosts + the renderer.
|
|
36
|
+
export { MAX_PIECES } from "./confetti-shader.js";
|
|
37
|
+
|
|
38
|
+
const DOPE = parseDope(doc as object);
|
|
39
|
+
|
|
40
|
+
// Registers the EffectFactory AND the bundled "confetti" program (so loadEffect()
|
|
41
|
+
// can bind a host-authored confetti variant — different counts/spread/palette —
|
|
42
|
+
// with no code). The whole factory is data + the two code-shaped hooks (shader +
|
|
43
|
+
// panel draw); resolve / reducedMotion / the MAX_PIECES const all come from the
|
|
44
|
+
// `.dope` via the shared registration tail.
|
|
45
|
+
export const confetti = registerDopePanelEffect(
|
|
46
|
+
DOPE,
|
|
47
|
+
{ vertex: CONFETTI_VERTEX_SRC, fragment: CONFETTI_FRAGMENT_SRC },
|
|
48
|
+
drawConfettiFrame,
|
|
49
|
+
) as unknown as EffectFactory<ConfettiRenderParams>;
|
|
50
|
+
|
|
51
|
+
export default confetti;
|