@dopaminefx/effect-solarbloom 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.
Files changed (38) hide show
  1. package/dist/check-fonts.d.ts +22 -0
  2. package/dist/check-fonts.d.ts.map +1 -0
  3. package/dist/check-fonts.js +19 -0
  4. package/dist/check-fonts.js.map +1 -0
  5. package/dist/check-renderer.d.ts +31 -0
  6. package/dist/check-renderer.d.ts.map +1 -0
  7. package/dist/check-renderer.js +102 -0
  8. package/dist/check-renderer.js.map +1 -0
  9. package/dist/index.d.ts +35 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +73 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/solarbloom-params.d.ts +48 -0
  14. package/dist/solarbloom-params.d.ts.map +1 -0
  15. package/dist/solarbloom-params.js +7 -0
  16. package/dist/solarbloom-params.js.map +1 -0
  17. package/dist/solarbloom-renderer.d.ts +40 -0
  18. package/dist/solarbloom-renderer.d.ts.map +1 -0
  19. package/dist/solarbloom-renderer.js +110 -0
  20. package/dist/solarbloom-renderer.js.map +1 -0
  21. package/dist/solarbloom-shader.d.ts +28 -0
  22. package/dist/solarbloom-shader.d.ts.map +1 -0
  23. package/dist/solarbloom-shader.js +447 -0
  24. package/dist/solarbloom-shader.js.map +1 -0
  25. package/dist/solarbloom-tempo.d.ts +13 -0
  26. package/dist/solarbloom-tempo.d.ts.map +1 -0
  27. package/dist/solarbloom-tempo.js +16 -0
  28. package/dist/solarbloom-tempo.js.map +1 -0
  29. package/dist/solarbloom.dope.json +552 -0
  30. package/package.json +46 -0
  31. package/src/check-fonts.ts +26 -0
  32. package/src/check-renderer.ts +109 -0
  33. package/src/index.ts +96 -0
  34. package/src/solarbloom-params.ts +50 -0
  35. package/src/solarbloom-renderer.ts +135 -0
  36. package/src/solarbloom-shader.ts +461 -0
  37. package/src/solarbloom-tempo.ts +18 -0
  38. package/src/solarbloom.dope.json +552 -0
@@ -0,0 +1,552 @@
1
+ {
2
+ "fmt": "dopamine-effect",
3
+ "v": "1.0.0",
4
+ "id": "dopamine.success.solarbloom",
5
+ "meta": {
6
+ "name": "Solarbloom",
7
+ "description": "A centered radial volumetric bloom — light radiating from a point — with drifting motes and a checkmark drawn in light.",
8
+ "tags": [
9
+ "success",
10
+ "bloom",
11
+ "radial"
12
+ ]
13
+ },
14
+ "controls": {
15
+ "mood": {
16
+ "type": "enum",
17
+ "label": "Mood",
18
+ "default": "celebratory",
19
+ "options": [
20
+ "serene",
21
+ "celebratory",
22
+ "electric"
23
+ ],
24
+ "ui": "segmented"
25
+ },
26
+ "intensity": {
27
+ "type": "scalar",
28
+ "label": "Intensity",
29
+ "default": 0.7,
30
+ "min": 0,
31
+ "max": 1,
32
+ "step": 0.01,
33
+ "ui": "slider",
34
+ "help": "Saturation, brightness, bloom size, overshoot."
35
+ },
36
+ "whimsy": {
37
+ "type": "scalar",
38
+ "label": "Whimsy",
39
+ "default": 0.5,
40
+ "min": 0,
41
+ "max": 1,
42
+ "step": 0.01,
43
+ "ui": "slider",
44
+ "help": "Photoreal (0) to cel / hand-drawn (1)."
45
+ },
46
+ "seed": {
47
+ "type": "int",
48
+ "label": "Seed",
49
+ "default": null,
50
+ "nullable": true,
51
+ "help": "Null = unique palette per fire; pin to reproduce."
52
+ },
53
+ "origin": {
54
+ "type": "point",
55
+ "label": "Origin",
56
+ "default": "center"
57
+ },
58
+ "target": {
59
+ "type": "selector",
60
+ "label": "Target",
61
+ "default": "document.body"
62
+ }
63
+ },
64
+ "baselines": {
65
+ "serene": {
66
+ "durationMs": 2600,
67
+ "lightness": 0.84,
68
+ "chroma": 0.09,
69
+ "hueCenter": 230,
70
+ "hueRange": 120,
71
+ "bloomRadius": 0.85,
72
+ "moteCount": 22,
73
+ "moteSpeed": 0.55,
74
+ "turbulence": 0.35,
75
+ "overshoot": 0.55,
76
+ "iridescence": 0.85,
77
+ "dispersion": 0.35
78
+ },
79
+ "celebratory": {
80
+ "durationMs": 1800,
81
+ "lightness": 0.8,
82
+ "chroma": 0.16,
83
+ "hueCenter": 50,
84
+ "hueRange": 320,
85
+ "bloomRadius": 0.7,
86
+ "moteCount": 48,
87
+ "moteSpeed": 0.85,
88
+ "turbulence": 0.6,
89
+ "overshoot": 1,
90
+ "iridescence": 0.6,
91
+ "dispersion": 0.6
92
+ },
93
+ "electric": {
94
+ "durationMs": 1200,
95
+ "lightness": 0.78,
96
+ "chroma": 0.23,
97
+ "hueCenter": 35,
98
+ "hueRange": 150,
99
+ "bloomRadius": 0.6,
100
+ "moteCount": 72,
101
+ "moteSpeed": 1.25,
102
+ "turbulence": 0.9,
103
+ "overshoot": 1.45,
104
+ "iridescence": 0.4,
105
+ "dispersion": 0.95
106
+ }
107
+ },
108
+ "palette": {
109
+ "model": "oklch",
110
+ "space": "linear-srgb",
111
+ "generator": "golden-angle",
112
+ "goldenAngleDeg": 137.50776405003785,
113
+ "stops": 3,
114
+ "hueSpread": 0.55,
115
+ "lightness": {
116
+ "baseline": "lightness",
117
+ "perStop": [
118
+ 0,
119
+ 0.06,
120
+ -0.05
121
+ ]
122
+ },
123
+ "chroma": {
124
+ "from": {
125
+ "mul": [
126
+ {
127
+ "baseline": "chroma"
128
+ },
129
+ {
130
+ "lerp": [
131
+ "intensity",
132
+ 0.7,
133
+ 1.5
134
+ ]
135
+ }
136
+ ]
137
+ },
138
+ "perStop": [
139
+ 0,
140
+ 0.02,
141
+ -0.01
142
+ ]
143
+ },
144
+ "seed": {
145
+ "deterministic": true,
146
+ "source": "controls.seed",
147
+ "prng": "mulberry32"
148
+ },
149
+ "perMood": {
150
+ "serene": {
151
+ "hueCenter": 230,
152
+ "hueRange": 120,
153
+ "lightness": 0.84,
154
+ "chroma": 0.09
155
+ },
156
+ "celebratory": {
157
+ "hueCenter": 50,
158
+ "hueRange": 320,
159
+ "lightness": 0.8,
160
+ "chroma": 0.16
161
+ },
162
+ "electric": {
163
+ "hueCenter": 35,
164
+ "hueRange": 150,
165
+ "lightness": 0.78,
166
+ "chroma": 0.23
167
+ }
168
+ }
169
+ },
170
+ "tempo": {
171
+ "durationMs": {
172
+ "from": {
173
+ "round": {
174
+ "mul": [
175
+ {
176
+ "baseline": "durationMs"
177
+ },
178
+ {
179
+ "lerp": [
180
+ "intensity",
181
+ 1.1,
182
+ 0.9
183
+ ]
184
+ }
185
+ ]
186
+ }
187
+ }
188
+ },
189
+ "frame": {
190
+ "amp": {
191
+ "envelope": [
192
+ {
193
+ "input": "life"
194
+ },
195
+ {
196
+ "param": "overshoot"
197
+ }
198
+ ]
199
+ },
200
+ "extras": {
201
+ "check": {
202
+ "easeOutCubic": {
203
+ "div": [
204
+ {
205
+ "input": "elapsedMs"
206
+ },
207
+ 240
208
+ ]
209
+ }
210
+ }
211
+ }
212
+ },
213
+ "reducedMotion": {
214
+ "peakMs": 260,
215
+ "holdMs": 360
216
+ },
217
+ "note": "Datafied per-frame logic (delta-0 with the historical hand frame() hook). amp = envelope(life, overshoot) — the held-breath success envelope (anticipation→peak→settle). check = checkProgress(elapsedMs) = easeOutCubic(elapsedMs/240): the functional confirmation draws within ~240 ms on the REAL (un-stepped) clock so it lands near the ~100 ms reward signal and reads smoothly even where the bloom/motes step on twos. The mote SPRITE PANEL geometry stays the one per-platform draw (the sprite-panel seam); the baked-SDF checkmark binds declaratively from binding.samplers on every platform (the fail precedent). The whimsy-picked glyph-fallback canvas is a web-only optional hook (the canonical effect always ships the baked SDF)."
218
+ },
219
+ "geometry": {
220
+ "kind": "radial",
221
+ "viewBox": [
222
+ 0,
223
+ 0,
224
+ 100,
225
+ 100
226
+ ],
227
+ "outlines": {
228
+ "checkmark": {
229
+ "role": "confirm-glyph",
230
+ "source": "baked-sdf",
231
+ "note": "GEOMETRY SEAM: the checkmark icon is driven by the svgPath below, baked at build time (scripts/bake-sdf.mjs → engine/sdf.ts) into the inline `sdf` distance field. The runtime samples that SDF (effects/solarbloom.ts + engine/shader.ts uSdfTex) — it never re-bakes. Change the svgPath and re-bake to change the rendered icon with NO shader edit. If `sdf` is absent the effect falls back to a bundled SIL-OFL check font glyph, then to the analytic two-segment SDF, so the win always confirms.",
232
+ "svgPath": "M 5 55 L 38 88 L 95 12",
233
+ "sdf": {
234
+ "size": 64,
235
+ "range": 18,
236
+ "viewBox": [
237
+ 0,
238
+ 0,
239
+ 100,
240
+ 100
241
+ ],
242
+ "data": "data:application/octet-stream;base64,RFMAQP/////////////////////////////////////////////////////////////////////z49PGua+nop+go6r////////////////////////////////////////////////////////////////////35dTDtKebkoyJio6V///////////////////////////////////////////////////////////////////76djGtaSViH12c3R5gf//////////////////////////////////////////////////////////////////7tzKuaeVhXZqYV1eZG7/////////////////////////////////////////////////////////////////8uHPvayaiHZlV0xHSFBc////////////////////////////////////////////////////////////////9+XTwrCejXtpV0Y4MTM9Tf//////////////////////////////////////////////////////////////++nYxrSjkX9uXEo4JxseLkH//////////////////////////////////////////////////////////////+7cyrmnlYRyYE89KxkIECY8//////////////////////////////////////////////////////////////Lhz72smoh2ZVNBMB4MBhcqPv////////////////////////////////////////////////////////////fl08Kwno17aVdGNCIRARMlNkj///////////////////////////////////////////////////////////vp2Ma0o5F/blxKOCcVAw4gMkRV///////////////////////////////////////////////////////////u3Mq5p5WEcmBPPSsZCAocLT9RY//////////////////////////////////////////////////////////y4c+9rJqIdmVTQTAeDAYXKTtMXnD////////////////////////////////////////////////////////35dPCsJ6Ne2lXRjQiEQETJTZIWmt9///////////////////////////////////////////////////////76djGtKORf25cSjgnFQMOIDJEVWd5iv//////////////////////////////////////////////////////7tzKuaeVhHJgTz0rGQgKHC0/UWN0hpj/////////////////////////////////////////////////////8uHPvayaiHZlU0EwHgwGFyk7TF5wgpOl////////////////////////////////////////////////////9+XTwrCejXtpV0Y0IhEBEyU2SFprfY+hsv//////////////////////////////////////////////////++nYxrSjkX9uXEo4JxUDDiAyRFVneYqcrsD//////////////////////////////////////////////////+7cyrmnlYRyYE89KxkIChwtP1FjdIaYqbvN//////////////////////////////////////////////////Lhz72smoh2ZVNBMB4MBhcpO0xecIKTpbfI2v////////////////////////////////////////////////fl08Kwno17aVdGNCIRARMlNkhaa32PobLE1uf///////////////////////////////////////////////vp2Ma0o5F/blxKOCcVAw4gMkRVZ3mKnK7A0eP1///////////////////////////////////////////////u3Mq5p5WEcmBPPSsZCAocLT9RY3SGmKm7zd/w//Tw7e3v8vj////////////////////////////////////y4c+9rJqIdmVTQTAeDAYXKTtMXnCCk6W3yNrs/f/f2tfX2d3j6/X////////////////////////////////35dPCsJ6Ne2lXRjQiEQETJTZIWmt9j6GyxNbn+f//ysTBwcPHztfh7vv////////////////////////////76djGtKORf25cSjgnFQMOIDJEVWd5ipyuwNHj9f///7Wvq6utsrnDz9zr+v//////////////////////////7tzKuaeVhHJgTz0rGQgKHC0/UWN0hpipu83f8P////+gmZWUl52lsL3M2+v6////////////////////////8uHPvayaiHZlU0EwHgwGFyk7TF5wgpOlt8ja7P3/////jIR/foGIkp6svMvb6/r/////////////////////9+XTwrCejXtpV0Y0IhEBEyU2SFprfY+hssTW5/n//////3hvaWhsdH+Nnay8y9vr+v//////////////////++nYxrSjkX9uXEo4JxUDDiAyRFVneYqcrsDR4/X///////9lWlNSV2BufY2drLzL2+v6/////////////////+7cyrmnlYRyYE89KxkIChwtP1FjdIaYqbvN3/D/////////VUc+PEJPXm59jZ2svMvb6/r///////////////Lhz72smoh2ZVNBMB4MBhcpO0xecIKTpbfI2uz9/////////0c1KSYvP05ebn2Nnay8y9vr+v////////////fl08Kwno17aVdGNCIRARMlNkhaa32PobLE1uf5//////////8+KRYRHy8/Tl5ufY2drLzL2+v6//////////vp2Ma0o5F/blxKOCcVAw4gMkRVZ3mKnK7A0eP1////////////PCYRABAfLz9OXm59jZ2svMvb6/r////////u3Mq5p5WEcmBPPSsZCAocLT9RY3SGmKm7zd/w/////////////0IvHxAAEB8vP05ebn2Nnay8y9vr+v/////y4c+9rJqIdmVTQTAeDAYXKTtMXnCCk6W3yNrs/f////////////9PPy8fEAAQHy8/Tl5ufY2drLzL2+v6///35dPCsJ6Ne2lXRjQiEQETJTZIWmt9j6GyxNbn+f//////////////Xk4/Lx8QABAfLz9OXm59jZ2svMvb6/r76djGtKORf25cSjgnFQMOIDJEVWd5ipyuwNHj9f///////////////25eTj8vHxAAEB8vP05ebn2Nnay8y9vr7tzKuaeVhHJgTz0rGQgKHC0/UWN0hpipu83f8P////////////////99bl5OPy8fEAAQHy8/Tl5ufY2drLzL2+HPvayaiHZlU0EwHgwGFyk7TF5wgpOlt8ja7P3/////////////////jX1uXk4/Lx8QABAfLz9OXm59jZ2svMvTwrCejXtpV0Y0IhEBEyU2SFprfY+hssTW5/n//////////////////52NfW5eTj8vHxAAEB8vP05ebn2Nnay8xrSjkX9uXEo4JxUDDiAyRFVneYqcrsDR4/X///////////////////+snY19bl5OPy8fEAAQHy8/Tl5ufY2drLmnlYRyYE89KxkIChwtP1FjdIaYqbvN3/D/////////////////////vKydjX1uXk4/Lx8QABAfLz9OXm59jZ2smoh2ZVNBMB4MBhcpO0xecIKTpbfI2uz9/////////////////////8u8rJ2NfW5eTj8vHxAAEB8vP05ebn2NnY17aVdGNCIRARMlNkhaa32PobLE1uf5///////////////////////by7ysnY19bl5OPy8fEAAQHy8/Tl5ufY1/blxKOCcVAw4gMkRVZ3mKnK7A0eP1////////////////////////69vLvKydjX1uXk4/Lx8QABAfLz9OXm59cmBPPSsZCAocLT9RY3SGmKm7zd/w//////////////////////////rr28u8rJ2NfW5eTj8vHxAAEB8vP05ebmVTQTAeDAYXKTtMXnCCk6W3yNrs/f//////////////////////////+uvby7ysnY19bl5OPy8fEAAQHy8/Tl5XRjQiEQETJTZIWmt9j6GyxNbn+f/////////////////////////////669vLvKydjX1uXk4/Lx8QABAfLz9OSjgnFQMOIDJEVWd5ipyuwNHj9f////////////////////////////////rr28u8rJ2NfW5eTj8vHxAAEB8vPz0rGQgKHC0/UWN0hpipu83f8P//////////////////////////////////+uvby7ysnY19bl5OPy8fEAAQHy8wHgwGFyk7TF5wgpOlt8ja7P3////////////////////////////////////669vLvKydjX1uXk4/Lx8QABAfIhEBEyU2SFprfY+hssTW5/n///////////////////////////////////////rr28u8rJ2NfW5eTj8vHxAAEBUDDiAyRFVneYqcrsDR4/X/////////////////////////////////////////+uvby7ysnY19bl5OPy8fEAAIChwtP1FjdIaYqbvN3/D////////////////////////////////////////////669vLvKydjX1uXk4/Lx8QBhcpO0xecIKTpbfI2uz9//////////////////////////////////////////////rr28u8rJ2NfW5eTj8vIBolNkhaa32PobLE1uf5////////////////////////////////////////////////+uvby7ysnY19bl5OPzQwN0RVZ3mKnK7A0eP1///////////////////////////////////////////////////669vLvKydjX1uXlFJR0tVZHSGmKm7zd/w//////////////////////////////////////////////////////rr28u8rJ2NfXBlXl1gaHSDk6W3yNrs/f//////////////////////////////////////////////////////+uvby7ysnY6DenRzdnyHk6KyxNbn+f/////////////////////////////////////////////////////////669vLvK2hlo+KiYuRmqWywdLj9f////////////////////////////////////////////////////////////rr282/tKukoJ+hpq64xNLh8f////////////////////////////////////////8="
243
+ }
244
+ }
245
+ }
246
+ },
247
+ "content": {
248
+ "note": "Whimsy→check-glyph fallback bands (face + char), used only when the baked SDF is absent. Equal-width bands: low whimsy = refined ✓, high = bold ✔. Reproduces engine/mood.ts pickCheckGlyph byte-for-byte.",
249
+ "glyphBands": [
250
+ {
251
+ "family": "Dopamine Check Symbols",
252
+ "char": "✓"
253
+ },
254
+ {
255
+ "family": "Dopamine Check Sans",
256
+ "char": "✔"
257
+ },
258
+ {
259
+ "family": "Dopamine Check Symbols",
260
+ "char": "✔"
261
+ }
262
+ ]
263
+ },
264
+ "render": {
265
+ "params": {
266
+ "exposure": {
267
+ "type": "float",
268
+ "from": {
269
+ "lerp": [
270
+ "intensity",
271
+ 0.75,
272
+ 1.5
273
+ ]
274
+ }
275
+ },
276
+ "bloomRadius": {
277
+ "type": "float",
278
+ "from": {
279
+ "mul": [
280
+ {
281
+ "baseline": "bloomRadius"
282
+ },
283
+ {
284
+ "lerp": [
285
+ "intensity",
286
+ 0.8,
287
+ 1.15
288
+ ]
289
+ }
290
+ ]
291
+ }
292
+ },
293
+ "moteCount": {
294
+ "type": "int",
295
+ "clampMax": "MAX_MOTES",
296
+ "from": {
297
+ "round": {
298
+ "mul": [
299
+ {
300
+ "baseline": "moteCount"
301
+ },
302
+ {
303
+ "lerp": [
304
+ "intensity",
305
+ 0.85,
306
+ 1.25
307
+ ]
308
+ }
309
+ ]
310
+ }
311
+ }
312
+ },
313
+ "moteSpeed": {
314
+ "type": "float",
315
+ "from": {
316
+ "baseline": "moteSpeed"
317
+ }
318
+ },
319
+ "turbulence": {
320
+ "type": "float",
321
+ "from": {
322
+ "mul": [
323
+ {
324
+ "baseline": "turbulence"
325
+ },
326
+ {
327
+ "lerp": [
328
+ "intensity",
329
+ 0.85,
330
+ 1.2
331
+ ]
332
+ }
333
+ ]
334
+ }
335
+ },
336
+ "overshoot": {
337
+ "type": "float",
338
+ "from": {
339
+ "mul": [
340
+ {
341
+ "baseline": "overshoot"
342
+ },
343
+ {
344
+ "lerp": [
345
+ "intensity",
346
+ 0.7,
347
+ 1.25
348
+ ]
349
+ }
350
+ ]
351
+ }
352
+ },
353
+ "iridescence": {
354
+ "type": "float",
355
+ "clamp01": true,
356
+ "from": {
357
+ "mul": [
358
+ {
359
+ "baseline": "iridescence"
360
+ },
361
+ {
362
+ "lerp": [
363
+ "whimsy",
364
+ 1,
365
+ 0.12
366
+ ]
367
+ }
368
+ ]
369
+ }
370
+ },
371
+ "dispersion": {
372
+ "type": "float",
373
+ "clamp01": true,
374
+ "from": {
375
+ "mul": [
376
+ {
377
+ "baseline": "dispersion"
378
+ },
379
+ {
380
+ "lerp": [
381
+ "whimsy",
382
+ 1,
383
+ 0.45
384
+ ]
385
+ },
386
+ {
387
+ "lerp": [
388
+ "intensity",
389
+ 0.85,
390
+ 1.1
391
+ ]
392
+ }
393
+ ]
394
+ }
395
+ },
396
+ "style": {
397
+ "type": "float",
398
+ "from": {
399
+ "control": "whimsy"
400
+ }
401
+ }
402
+ },
403
+ "shadowHeightFrac": {
404
+ "param": "bloomRadius"
405
+ },
406
+ "pass": {
407
+ "note": "PER-PASS uniforms (evaluated once per pass, never per frame): the checkmark glyph box half-size + SDF stroke/range in device px, sized to the TARGETED element (targetMinDimPx falls back to the full canvas when untargeted). sdfRange/sdfViewBoxW come from the checkmark sampler's baked-SDF metadata; sdfOn / checkTexOn are NOT here — the web aux-texture binding flips them (the baked SDF flips sdfOn, the glyph-canvas hook flips checkTexOn), natives keep the analytic ✓ at 0.",
408
+ "checkBox": {
409
+ "mul": [
410
+ 0.16,
411
+ {
412
+ "input": "targetMinDimPx"
413
+ }
414
+ ]
415
+ },
416
+ "sdfStrokePx": {
417
+ "mul": [
418
+ 0.16,
419
+ {
420
+ "input": "targetMinDimPx"
421
+ },
422
+ 0.11
423
+ ]
424
+ },
425
+ "sdfRangePx": {
426
+ "mul": [
427
+ {
428
+ "input": "sdfRange"
429
+ },
430
+ {
431
+ "div": [
432
+ {
433
+ "mul": [
434
+ 2,
435
+ {
436
+ "mul": [
437
+ 0.16,
438
+ {
439
+ "input": "targetMinDimPx"
440
+ }
441
+ ]
442
+ }
443
+ ]
444
+ },
445
+ {
446
+ "max": [
447
+ {
448
+ "input": "sdfViewBoxW"
449
+ },
450
+ 0.000001
451
+ ]
452
+ }
453
+ ]
454
+ }
455
+ ]
456
+ }
457
+ },
458
+ "panel": {
459
+ "sampler": "uMotePanel",
460
+ "texture": 3,
461
+ "note": "SPRITE-PANEL seam (a PASS effect's dynamic sprite layer, NOT a panel-kind whole-content panel): the drifting light motes are rasterized into an offscreen Canvas2D/CoreGraphics/android.graphics panel each frame (each mote's pose + lit colour + streak + twinkle computed ONCE per frame) and the shader SAMPLES it for the mote layer; the volumetric bloom + checkmark stay procedural in the shader. The panel binds at texture(3) so texture(1) stays the baked-SDF slot (the checkmark) — this PASS hybrid keeps BOTH the sprite panel AND the aux SDF on EVERY platform: the generic pass runner (web pass-runner / Metal MetalPassRunner+MetalOverlayHost / Android GlPassRunner) hosts a sprite panel at an arbitrary unit ALONGSIDE the baked-SDF aux (the heartburst sprite-panel + fail SDF-aux seams, composed). The draw itself is the per-platform sprite-panel-draw seam (web panelDraw hook / SolarbloomPanel.swift / SolarbloomPanel.kt) — the ONLY hand-written per-platform source."
462
+ },
463
+ "consts": {
464
+ "MAX_MOTES": 80
465
+ },
466
+ "config": {
467
+ "usesOrigin": true
468
+ },
469
+ "backends": {
470
+ "webgl2": {
471
+ "stage": "fullscreen-triangle",
472
+ "blend": "screen",
473
+ "shader": {
474
+ "program": "solarbloom"
475
+ }
476
+ }
477
+ },
478
+ "fallbackOrder": [
479
+ "webgl2"
480
+ ]
481
+ },
482
+ "binding": {
483
+ "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.",
484
+ "excludeParams": [
485
+ "style",
486
+ "overshoot",
487
+ "durationMs"
488
+ ],
489
+ "scatterKey": "moteSeed",
490
+ "scatterWeb": "uMoteSeed",
491
+ "extras": [
492
+ {
493
+ "name": "check",
494
+ "type": "float",
495
+ "web": "uCheck",
496
+ "note": "checkProgress(elapsedMs) (tempo.frame.extras)"
497
+ },
498
+ {
499
+ "name": "checkBox",
500
+ "type": "float",
501
+ "web": "uCheckBox",
502
+ "note": "half-size (device px) of glyph box (render.pass)"
503
+ },
504
+ {
505
+ "name": "checkTexOn",
506
+ "type": "float",
507
+ "web": "uCheckTexOn",
508
+ "note": "1 = sample the font glyph texture (flipped by the glyph-canvas aux hook)"
509
+ },
510
+ {
511
+ "name": "sdfOn",
512
+ "type": "float",
513
+ "web": "uSdfOn",
514
+ "note": "1 = drive the checkmark from the baked SDF (flipped by the SDF sampler binding)"
515
+ },
516
+ {
517
+ "name": "sdfRangePx",
518
+ "type": "float",
519
+ "web": "uSdfRangePx",
520
+ "note": "device px mapping to the SDF 0..1 range (render.pass)"
521
+ },
522
+ {
523
+ "name": "sdfStrokePx",
524
+ "type": "float",
525
+ "web": "uSdfStrokePx",
526
+ "note": "half stroke width (device px) (render.pass)"
527
+ }
528
+ ],
529
+ "samplers": [
530
+ {
531
+ "web": "uCheckTex",
532
+ "name": "checkTex",
533
+ "texture": 0,
534
+ "note": "GLYPH-FALLBACK seam: the whimsy-picked bundled check-font glyph (✓ / ✔), rasterized into an offscreen canvas and uploaded by an optional host glyph-fallback hook, used only when the baked SDF is absent. Code-shaped (the host font rasterize) — stays a hook; the canonical effect always ships the baked SDF, and natives don't bind aux textures (analytic fallback)."
535
+ },
536
+ {
537
+ "web": "uSdfTex",
538
+ "name": "sdfTex",
539
+ "texture": 1,
540
+ "outline": "checkmark",
541
+ "on": "sdfOn",
542
+ "note": "Declarative SDF source: the baked geometry.outlines.checkmark SDF binds here on EVERY platform (web, Metal, GL — the generic pass runner decodes the inline SDF, uploads an R8 texture and flips the sdfOn extra to 1); the analytic two-segment ✓ is the fallback when the SDF is absent (the fail precedent)."
543
+ },
544
+ {
545
+ "web": "uMotePanel",
546
+ "name": "motePanel",
547
+ "texture": 3,
548
+ "note": "SPRITE-PANEL sampler (render.panel): the drifting-mote Canvas2D/CoreGraphics/android.graphics panel the runner redraws + uploads every frame at texture(3), leaving texture(1) as the baked-SDF slot. Bound by the pass runner's sprite-panel seam on every platform; the per-platform mote DRAW is the panel-draw seam (web panelDraw hook / SolarbloomPanel.swift / SolarbloomPanel.kt)."
549
+ }
550
+ ]
551
+ }
552
+ }
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@dopaminefx/effect-solarbloom",
3
+ "version": "0.1.0",
4
+ "description": "Solarbloom — a centered radial volumetric bloom success effect for Dopamine.",
5
+ "keywords": [
6
+ "dopamine-effect"
7
+ ],
8
+ "type": "module",
9
+ "main": "./dist/index.js",
10
+ "module": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "src"
21
+ ],
22
+ "sideEffects": [
23
+ "./src/index.ts",
24
+ "./dist/index.js"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsc -p tsconfig.json"
28
+ },
29
+ "dependencies": {
30
+ "@dopaminefx/core": "^0.1.0"
31
+ },
32
+ "license": "MIT",
33
+ "author": "10in30",
34
+ "homepage": "https://github.com/10in30/dopamine#readme",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/10in30/dopamine.git",
38
+ "directory": "effects/solarbloom/web"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/10in30/dopamine/issues"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ }
46
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Generated module: CHECK-GLYPH woff2 subsets bundled as base64 so the Solarbloom
3
+ * success effect renders its checkmark from a REAL typeface glyph (✓ U+2713 /
4
+ * ✔ U+2714) and never silently depends on a host font being installed.
5
+ * Regenerate with scripts/embed-check-fonts.mjs.
6
+ *
7
+ * Faces (SIL Open Font License 1.1 — see assets/fonts/OFL.txt), subset to the two
8
+ * check codepoints only:
9
+ * Dopamine Check Sans — Source Sans 3 (humanist, refined ✓ / clean ✔)
10
+ * Dopamine Check Symbols — Noto Sans Symbols 2 (calligraphic ✓ / fat playful ✔)
11
+ *
12
+ * Solarbloom selects (face, codepoint) by WHIMSY (see CHECK_GLYPHS in
13
+ * engine/mood.ts): low whimsy = a refined/elegant check, high = a bold/playful one.
14
+ */
15
+
16
+ export interface CheckFace {
17
+ /** CSS font-family name to register + use. */
18
+ readonly family: string;
19
+ /** base64-encoded woff2 payload (subset to U+2713 / U+2714 only). */
20
+ readonly base64: string;
21
+ }
22
+
23
+ export const CHECK_FACES: readonly CheckFace[] = [
24
+ { family: "Dopamine Check Sans", base64: "d09GMgABAAAAAAKMABAAAAAABKwAAAIxAAMNUAAAAAAAAAAAAAAAAAAAAAAAAAAAGUYbIBw2BmA/U1RBVEQANBEICoFkgVoBNgIkAwwLCAAEIAWBAgcgDAcbhANgjtFLn/JCEEKZZ6ZUBNF+/Gb37ot6UqyJNVGPFjqhW6JSSZFIEm1U3tWabpIiOCS8a/nmc2FWJXA/lf2ML6FrhCmgIkvjq/pmO93fbj6RYSwBFlCkCQZWRlkyswwTDECTQCfDCeJreMOwoUA7ql75WEy1Bq1aA15H2npGIOqu3aCCqIcGqoSoZ8SkgSitIKR23aIZI0Cg07RZYV37qdDwsNDvsOs0p3BKK8UCfs2FxOBNvk4M4GgyixtQFGlkVyGqlszq2w4eaeqH6/TIteEK/3WgfIgEBMSvZDGvRQjtEsMSy/KVVGYSJPD4OVEJBELVLyC2EgMoQsYuECBRMKwIDKvmUVXB2nDTsDbUNJ//QoAQDa8PFNCKNuzFIwAqaBGitraWMjzU5d1T7wSOjQWMoECEAkYxGqOikdHjl5c65HkeDXyVDo8+5SaaqDAF7h45wv6xtOuhXJrLeOXdzXurudfzsacA2+WaG83dEOdW38GbofL1O2XToV0Frvr5sQavqMuduxMc3aDt9R0tK1f6r7QNH4dwLqmYZqy9ykqrwiv+1eSTdu/DyJwbOclnxjYQKD2nwtwrGTdDa/61l/IdvnTtXQK+dlVfqc1v7uQJWiUIHlby/Zj+BwKHlHzvXFrAK9/rFihGtYAgr8UhIlE7pF6KIEK3XszLpG3pt3phu3Ta4wxKykyFMOhkQqjIMRBKTkrGZBilJtdES0VsiJYaoSECmhs7UH8KfunQawsAAA==" },
25
+ { family: "Dopamine Check Symbols", base64: "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" },
26
+ ];