@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,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confetti Canvas2D PANEL drawing — the PANEL-DRAW SEAM.
|
|
3
|
+
*
|
|
4
|
+
* Confetti is a PANEL HYBRID (like comic/heartburst): the paper pieces are
|
|
5
|
+
* rasterized into an offscreen Canvas2D panel each frame (each pose computed
|
|
6
|
+
* ONCE per frame, in JS) and the fragment shader (confetti-shader.ts) samples
|
|
7
|
+
* that texture and applies the screen-space finish (the global gain, ACES
|
|
8
|
+
* tonemap, cel posterize, ordered dither, soft cast shadow). This is now
|
|
9
|
+
* CONVERGED across all three platforms: the native ConfettiPanel files
|
|
10
|
+
* (CoreGraphics / android.graphics) are faithful ports of this draw, the shader
|
|
11
|
+
* is single-source GLSL, and the factory / tempo / uniforms are GENERATED from
|
|
12
|
+
* confetti.dope.json. The pieces' MOTION (the ballistic launch-then-fall poses)
|
|
13
|
+
* is panel GEOMETRY — code by design — so this draw is the ONE hand-written web
|
|
14
|
+
* source beyond the shader (the per-platform panel-draw seam).
|
|
15
|
+
*
|
|
16
|
+
* PERFORMANCE: the original single-pass design re-derived all MAX_PIECES poses
|
|
17
|
+
* (hash + ballistic + sway + spin) at EVERY pixel — O(pixels × pieces), fine on
|
|
18
|
+
* a GPU but crawling under software/ANGLE WebGL. The pieces cover a tiny
|
|
19
|
+
* fraction of the screen, so the work belongs where it scales with COVERED
|
|
20
|
+
* AREA, not pixel count.
|
|
21
|
+
*
|
|
22
|
+
* Panel channel encoding consumed by confetti-shader.ts:
|
|
23
|
+
* RGB = the per-piece LIT colour (palette × paper/cel shading), pre-multiplied
|
|
24
|
+
* by the piece's lifetime fade and accumulated additively across pieces.
|
|
25
|
+
* The shader applies the global gain (amp × exposure), tonemap + finish.
|
|
26
|
+
*/
|
|
27
|
+
import { type PanelDraw, type RGB } from "@dopaminefx/core";
|
|
28
|
+
/** Resolved render params the confetti panel consumes. */
|
|
29
|
+
export interface ConfettiRenderParams {
|
|
30
|
+
durationMs: number;
|
|
31
|
+
palette: RGB[];
|
|
32
|
+
style: number;
|
|
33
|
+
exposure: number;
|
|
34
|
+
pieceCount: number;
|
|
35
|
+
spread: number;
|
|
36
|
+
launchSpeed: number;
|
|
37
|
+
gravity: number;
|
|
38
|
+
flutter: number;
|
|
39
|
+
pieceSize: number;
|
|
40
|
+
spin: number;
|
|
41
|
+
overshoot: number;
|
|
42
|
+
pieceSeed: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Draw one frame of confetti into the offscreen panel. `life` is whole-effect
|
|
46
|
+
* progress 0..1; `center` is the launch anchor (device px, canvas y-down).
|
|
47
|
+
*
|
|
48
|
+
* Mirrors the original shader's `pieceAt` motion (a mostly-up launch cone, a
|
|
49
|
+
* gravity arc, an air-drag sway, and a tumbling spin with a face-flash) and its
|
|
50
|
+
* per-piece lit colour (paper shading ↔ flat cel by whimsy), computed once per
|
|
51
|
+
* piece in JS instead of once per pixel in GLSL.
|
|
52
|
+
*/
|
|
53
|
+
/**
|
|
54
|
+
* The per-frame panel draw in the generic `PanelDraw` shape — the ONE
|
|
55
|
+
* code-shaped hook the data-driven panel factory wires
|
|
56
|
+
* (`registerDopePanelEffect`). The whole-effect amplitude envelope is DATA
|
|
57
|
+
* (`tempo.frame.amp` in confetti.dope.json); this draw owns only the pieces'
|
|
58
|
+
* panel GEOMETRY (the ballistic launch-then-fall poses), which is code by
|
|
59
|
+
* design. Hands off to {@link drawConfettiPanel}.
|
|
60
|
+
*/
|
|
61
|
+
export declare const drawConfettiFrame: PanelDraw;
|
|
62
|
+
export declare function drawConfettiPanel(ctx: CanvasRenderingContext2D, w: number, h: number, params: ConfettiRenderParams, life: number, center: {
|
|
63
|
+
x: number;
|
|
64
|
+
y: number;
|
|
65
|
+
}): void;
|
|
66
|
+
//# sourceMappingURL=confetti-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confetti-renderer.d.ts","sourceRoot":"","sources":["../src/confetti-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAc,KAAK,SAAS,EAAE,KAAK,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAGxE,0DAA0D;AAC1D,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,GAAG,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAuBD;;;;;;;;GAQG;AACH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAE/B,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,wBAAwB,EAC7B,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,MAAM,EAAE,oBAAoB,EAC5B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B,IAAI,CAgGN"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confetti Canvas2D PANEL drawing — the PANEL-DRAW SEAM.
|
|
3
|
+
*
|
|
4
|
+
* Confetti is a PANEL HYBRID (like comic/heartburst): the paper pieces are
|
|
5
|
+
* rasterized into an offscreen Canvas2D panel each frame (each pose computed
|
|
6
|
+
* ONCE per frame, in JS) and the fragment shader (confetti-shader.ts) samples
|
|
7
|
+
* that texture and applies the screen-space finish (the global gain, ACES
|
|
8
|
+
* tonemap, cel posterize, ordered dither, soft cast shadow). This is now
|
|
9
|
+
* CONVERGED across all three platforms: the native ConfettiPanel files
|
|
10
|
+
* (CoreGraphics / android.graphics) are faithful ports of this draw, the shader
|
|
11
|
+
* is single-source GLSL, and the factory / tempo / uniforms are GENERATED from
|
|
12
|
+
* confetti.dope.json. The pieces' MOTION (the ballistic launch-then-fall poses)
|
|
13
|
+
* is panel GEOMETRY — code by design — so this draw is the ONE hand-written web
|
|
14
|
+
* source beyond the shader (the per-platform panel-draw seam).
|
|
15
|
+
*
|
|
16
|
+
* PERFORMANCE: the original single-pass design re-derived all MAX_PIECES poses
|
|
17
|
+
* (hash + ballistic + sway + spin) at EVERY pixel — O(pixels × pieces), fine on
|
|
18
|
+
* a GPU but crawling under software/ANGLE WebGL. The pieces cover a tiny
|
|
19
|
+
* fraction of the screen, so the work belongs where it scales with COVERED
|
|
20
|
+
* AREA, not pixel count.
|
|
21
|
+
*
|
|
22
|
+
* Panel channel encoding consumed by confetti-shader.ts:
|
|
23
|
+
* RGB = the per-piece LIT colour (palette × paper/cel shading), pre-multiplied
|
|
24
|
+
* by the piece's lifetime fade and accumulated additively across pieces.
|
|
25
|
+
* The shader applies the global gain (amp × exposure), tonemap + finish.
|
|
26
|
+
*/
|
|
27
|
+
import { mulberry32 } from "@dopaminefx/core";
|
|
28
|
+
import { MAX_PIECES } from "./confetti-shader.js";
|
|
29
|
+
const TAU = Math.PI * 2;
|
|
30
|
+
const clamp01 = (x) => (x < 0 ? 0 : x > 1 ? 1 : x);
|
|
31
|
+
const mix = (a, b, t) => a + (b - a) * t;
|
|
32
|
+
const fract = (x) => x - Math.floor(x);
|
|
33
|
+
const smoothstep = (e0, e1, x) => {
|
|
34
|
+
const t = clamp01((x - e0) / (e1 - e0));
|
|
35
|
+
return t * t * (3 - 2 * t);
|
|
36
|
+
};
|
|
37
|
+
/** paletteMix from the look lib: two-segment lerp across the three stops. */
|
|
38
|
+
function paletteMix(pal, t) {
|
|
39
|
+
t = clamp01(t);
|
|
40
|
+
const [c0, c1, c2] = pal;
|
|
41
|
+
if (t < 0.5) {
|
|
42
|
+
const k = t * 2;
|
|
43
|
+
return { r: mix(c0.r, c1.r, k), g: mix(c0.g, c1.g, k), b: mix(c0.b, c1.b, k) };
|
|
44
|
+
}
|
|
45
|
+
const k = (t - 0.5) * 2;
|
|
46
|
+
return { r: mix(c1.r, c2.r, k), g: mix(c1.g, c2.g, k), b: mix(c1.b, c2.b, k) };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Draw one frame of confetti into the offscreen panel. `life` is whole-effect
|
|
50
|
+
* progress 0..1; `center` is the launch anchor (device px, canvas y-down).
|
|
51
|
+
*
|
|
52
|
+
* Mirrors the original shader's `pieceAt` motion (a mostly-up launch cone, a
|
|
53
|
+
* gravity arc, an air-drag sway, and a tumbling spin with a face-flash) and its
|
|
54
|
+
* per-piece lit colour (paper shading ↔ flat cel by whimsy), computed once per
|
|
55
|
+
* piece in JS instead of once per pixel in GLSL.
|
|
56
|
+
*/
|
|
57
|
+
/**
|
|
58
|
+
* The per-frame panel draw in the generic `PanelDraw` shape — the ONE
|
|
59
|
+
* code-shaped hook the data-driven panel factory wires
|
|
60
|
+
* (`registerDopePanelEffect`). The whole-effect amplitude envelope is DATA
|
|
61
|
+
* (`tempo.frame.amp` in confetti.dope.json); this draw owns only the pieces'
|
|
62
|
+
* panel GEOMETRY (the ballistic launch-then-fall poses), which is code by
|
|
63
|
+
* design. Hands off to {@link drawConfettiPanel}.
|
|
64
|
+
*/
|
|
65
|
+
export const drawConfettiFrame = (pctx, w, h, params, info) => {
|
|
66
|
+
drawConfettiPanel(pctx, w, h, params, info.life, info.centerPx);
|
|
67
|
+
};
|
|
68
|
+
export function drawConfettiPanel(ctx, w, h, params, life, center) {
|
|
69
|
+
ctx.clearRect(0, 0, w, h);
|
|
70
|
+
if (life <= 0 || life >= 1)
|
|
71
|
+
return;
|
|
72
|
+
const minDim = Math.min(w, h);
|
|
73
|
+
const count = Math.max(0, Math.min(MAX_PIECES, Math.round(params.pieceCount)));
|
|
74
|
+
const rng = mulberry32(((params.pieceSeed * 1000) >>> 0) + 1);
|
|
75
|
+
const style = params.style;
|
|
76
|
+
ctx.save();
|
|
77
|
+
ctx.globalCompositeOperation = "lighter"; // additive accumulation, like the shader's `col +=`
|
|
78
|
+
for (let i = 0; i < count; i++) {
|
|
79
|
+
// Five per-piece randoms in a fixed order (≈ the GLSL hash21/hash11 draws).
|
|
80
|
+
const hx = rng(), hy = rng(), h2x = rng(), h2y = rng(), h3 = rng();
|
|
81
|
+
// Spawn stagger: most pieces fire in the first ~12%, renormalized to a full arc.
|
|
82
|
+
const delay = h2x * 0.12;
|
|
83
|
+
const pl = clamp01((life - delay) / (1 - delay));
|
|
84
|
+
if (pl <= 0 || pl >= 1)
|
|
85
|
+
continue;
|
|
86
|
+
// Launch direction (y-up local frame): a mostly-up cone fanned by spread.
|
|
87
|
+
const fan = (hx - 0.5) * 2;
|
|
88
|
+
const dlen = Math.hypot(fan * (0.35 + params.spread), 1.0);
|
|
89
|
+
const dirx = (fan * (0.35 + params.spread)) / dlen;
|
|
90
|
+
const diry = 1.0 / dlen;
|
|
91
|
+
const speed = (0.85 + hy * 0.6) * params.launchSpeed * minDim * 1.15;
|
|
92
|
+
const grav = (0.9 + h3 * 0.4) * params.gravity * minDim * 1.5;
|
|
93
|
+
// Ballistic arc: up, then down under gravity (y-up).
|
|
94
|
+
let px = dirx * speed * pl;
|
|
95
|
+
let py = diry * speed * pl - grav * pl * pl;
|
|
96
|
+
// Air-drag flutter: a growing sideways sway as the piece slows + falls.
|
|
97
|
+
const swayPhase = hx * TAU + h2y * 3.0;
|
|
98
|
+
const swayFreq = 3.0 + h2x * 4.0;
|
|
99
|
+
const fallT = smoothstep(0.12, 0.7, pl);
|
|
100
|
+
const swayAmp = params.flutter * minDim * 0.06 * (0.4 + fallT);
|
|
101
|
+
const sway = Math.sin(pl * swayFreq + swayPhase) * swayAmp +
|
|
102
|
+
Math.sin(pl * swayFreq * 0.37 + swayPhase * 1.7) * swayAmp * 0.4;
|
|
103
|
+
px += sway;
|
|
104
|
+
// Spin + face-flash (wide/bright face-on, dim edge-on).
|
|
105
|
+
const spinRate = (3.0 + h3 * 6.0) * params.spin;
|
|
106
|
+
const rot = pl * spinRate * TAU + swayPhase;
|
|
107
|
+
const flip = Math.abs(Math.cos(rot * 0.5 + sway * 0.02));
|
|
108
|
+
const face = mix(0.18, 1.0, flip);
|
|
109
|
+
// Paper shape: rectangles + a few petals, foreshortened by the face angle.
|
|
110
|
+
const aspect = mix(0.5, 1.6, h2y);
|
|
111
|
+
const s = minDim * 0.011 * params.pieceSize * (0.7 + hy * 0.7);
|
|
112
|
+
const fore = mix(1.0, face, 0.65);
|
|
113
|
+
const heX = Math.max(s * aspect * fore, 0.5);
|
|
114
|
+
const heY = Math.max(s * fore, 0.5);
|
|
115
|
+
const hue = fract(h2y * 0.9 + h3 * 0.31);
|
|
116
|
+
const petal = h3 >= 0.78;
|
|
117
|
+
// Per-piece lit colour (paper shading ↔ flat cel), pre-multiplied by fade.
|
|
118
|
+
const base = paletteMix(params.palette, hue);
|
|
119
|
+
const shade = mix(0.45, 1.15, face);
|
|
120
|
+
const spec = smoothstep(0.85, 1.0, face) * 0.5;
|
|
121
|
+
const celK = face >= 0.5 ? 1 : 0;
|
|
122
|
+
const celShade = mix(0.55, 1.1, celK);
|
|
123
|
+
const fade = (1 - Math.pow(pl, 1.4)) * smoothstep(0.0, 0.08, pl);
|
|
124
|
+
const lit = (c) => {
|
|
125
|
+
const paper = c * shade + spec;
|
|
126
|
+
const cel = c * celShade;
|
|
127
|
+
return clamp01(mix(paper, cel, style)) * fade;
|
|
128
|
+
};
|
|
129
|
+
const r = Math.round(lit(base.r) * 255);
|
|
130
|
+
const g = Math.round(lit(base.g) * 255);
|
|
131
|
+
const bl = Math.round(lit(base.b) * 255);
|
|
132
|
+
if (r + g + bl <= 0)
|
|
133
|
+
continue;
|
|
134
|
+
// Place in canvas space (flip y: local y-up → canvas y-down).
|
|
135
|
+
const cx = center.x + px;
|
|
136
|
+
const cy = center.y - py;
|
|
137
|
+
ctx.save();
|
|
138
|
+
ctx.translate(cx, cy);
|
|
139
|
+
ctx.rotate(rot);
|
|
140
|
+
ctx.fillStyle = `rgb(${r},${g},${bl})`;
|
|
141
|
+
if (petal) {
|
|
142
|
+
ctx.beginPath();
|
|
143
|
+
ctx.ellipse(0, 0, heX * 1.05, heY * 1.05, 0, 0, TAU);
|
|
144
|
+
ctx.fill();
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
const rad = Math.min(heX, heY) * 0.5;
|
|
148
|
+
ctx.beginPath();
|
|
149
|
+
ctx.roundRect(-heX, -heY, heX * 2, heY * 2, rad);
|
|
150
|
+
ctx.fill();
|
|
151
|
+
}
|
|
152
|
+
ctx.restore();
|
|
153
|
+
}
|
|
154
|
+
ctx.restore();
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=confetti-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confetti-renderer.js","sourceRoot":"","sources":["../src/confetti-renderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLSL ES 3.00 source for Confetti (web, PANEL architecture).
|
|
3
|
+
*
|
|
4
|
+
* The paper pieces are rasterized into an offscreen Canvas2D panel each frame
|
|
5
|
+
* (see confetti-renderer.ts) — each piece's pose + lit colour computed ONCE in JS
|
|
6
|
+
* rather than re-derived at every pixel. This shader is now a cheap O(pixels)
|
|
7
|
+
* pass: it SAMPLES that panel and applies only the screen-space finish that wants
|
|
8
|
+
* to be procedural —
|
|
9
|
+
* - the global gain (envelope amp × exposure) + filmic ACES tonemap so dense
|
|
10
|
+
* bursts roll off gracefully on the screen-blend page,
|
|
11
|
+
* - the cel posterize / saturation punch toward the whimsy (cel) end,
|
|
12
|
+
* - an ordered dither to kill screen-blend banding,
|
|
13
|
+
* - and a cheap soft drop-shadow on the multiply pass (a ring-blurred sample of
|
|
14
|
+
* the panel's mass, offset toward the implied light).
|
|
15
|
+
*
|
|
16
|
+
* Why: the old single-pass design looped MAX_PIECES at every fragment
|
|
17
|
+
* (O(pixels × pieces)) which is fine on a GPU but crawls under software/ANGLE
|
|
18
|
+
* WebGL. Sampling a pre-rasterized panel makes the per-pixel cost independent of
|
|
19
|
+
* piece count. Mirrors the comic/heartburst hybrid effects. (The Swift/Metal
|
|
20
|
+
* confetti keeps its analytic GPU pass — this change is web-only; the .dope is
|
|
21
|
+
* unchanged across platforms.)
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Max confetti pieces. Single source of truth: BOTH the panel renderer's loop
|
|
25
|
+
* bound and the integer-clamp const the `.dope` mapping references (passed to the
|
|
26
|
+
* loader as `MAX_PIECES`). Counts above this won't render.
|
|
27
|
+
*/
|
|
28
|
+
export declare const MAX_PIECES = 120;
|
|
29
|
+
export declare const CONFETTI_VERTEX_SRC = "#version 300 es\nout vec2 vUv;\nvoid main() {\n // Single full-screen triangle from gl_VertexID \u2014 no vertex buffers needed.\n vec2 pos = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));\n vUv = pos;\n gl_Position = vec4(pos * 2.0 - 1.0, 0.0, 1.0);\n}";
|
|
30
|
+
export declare const CONFETTI_FRAGMENT_SRC = "#version 300 es\nprecision highp float;\nin vec2 vUv;\nout vec4 fragColor;\n\nuniform sampler2D uPanel; // RGB = accumulated per-piece lit colour \u00D7 fade\nuniform vec2 uResolution; // device pixels\nuniform float uAmp; // envelope amplitude (peaks > 1) \u2014 overall brightness\nuniform float uTimeS; // elapsed seconds (snapped \"on twos\" with style)\nuniform float uExposure;\nuniform float uStyle; // 0..1 photoreal paper -> flat cel shapes\nuniform float uShadow; // 0 = light pass (screen), 1 = shadow pass (multiply)\nuniform vec2 uShadowOffset; // device-px offset of the cast silhouette\nuniform float uShadowSoft; // penumbra softness in device px (blur tap radius)\nuniform float uShadowStrength;// 0..1 max darkening of the multiply layer\nuniform vec3 uC0;\n\n\n#define TAU 6.28318530718\n\n\nfloat hash11(float p){ p = fract(p * 0.1031); p *= p + 33.33; p *= p + p; return fract(p); }\nvec2 hash21(float p){\n vec3 p3 = fract(vec3(p) * vec3(0.1031, 0.1030, 0.0973));\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\n\nvec3 tonemapACES(vec3 x){\n const float a = 2.51, b = 0.03, c = 2.43, d = 0.59, e = 0.14;\n return clamp((x * (a * x + b)) / (x * (c * x + d) + e), 0.0, 1.0);\n}\n\n\nvec3 ditherAdd(vec3 col, vec2 frag, float t, float fade){\n float dz = hash11(dot(frag, vec2(12.989, 78.233)) + t) - 0.5;\n return col + (dz / 255.0) * fade;\n}\n\n\nvoid main() {\n vec2 frag = vUv * uResolution;\n\n // ---- SHADOW pass (multiply layer) --------------------------------------\n // A cheap soft drop-shadow: ring-blur the panel's mass at a sample point pushed\n // against the light offset. White = no shadow; darker = cast silhouette.\n if (uShadow > 0.5) {\n vec2 px = 1.0 / uResolution;\n vec2 souv = vUv - uShadowOffset * px;\n float occ = 0.0;\n for (int i = 0; i < 8; i++) {\n float a = float(i) / 8.0 * TAU;\n vec2 o = vec2(cos(a), sin(a)) * uShadowSoft * px;\n vec2 tuv = souv + o;\n vec2 inb = step(vec2(0.0), tuv) * step(tuv, vec2(1.0));\n vec3 s = texture(uPanel, tuv).rgb;\n occ += (s.r + s.g + s.b) * (1.0 / 3.0) * inb.x * inb.y;\n }\n occ /= 8.0;\n float dark = clamp(occ * uAmp, 0.0, 1.0) * uShadowStrength;\n vec3 tint = mix(vec3(1.0), 0.6 + 0.4 * normalize(uC0 + 1e-3), 0.2);\n vec3 mul = mix(vec3(1.0), tint, dark);\n fragColor = vec4(mul, 1.0);\n return;\n }\n\n // ---- LIGHT pass --------------------------------------------------------\n // The panel already holds \u03A3(lit \u00D7 fade) per piece; apply the global gain, then\n // the same filmic + cel + dither finish the original single-pass shader did.\n vec3 col = texture(uPanel, vUv).rgb * (uAmp * uExposure) * 1.35;\n\n col = tonemapACES(col * 0.85);\n\n // Cel posterize at the whimsy end: punch saturation + quantize into hard bands.\n if (uStyle > 0.001) {\n float l = dot(col, vec3(0.299, 0.587, 0.114));\n vec3 neon = clamp(l + (col - l) * 1.5, 0.0, 1.0);\n vec3 styled = mix(col, neon, 0.65);\n float bands = mix(40.0, 5.0, uStyle);\n styled = floor(styled * bands + 0.5) / bands;\n col = mix(col, styled, uStyle);\n }\n\n // Ordered dither to break screen-blend banding; faded toward the cel end.\n col = ditherAdd(col, frag, uTimeS, 1.0 - uStyle);\n\n fragColor = vec4(col, 1.0);\n}";
|
|
31
|
+
//# sourceMappingURL=confetti-shader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confetti-shader.d.ts","sourceRoot":"","sources":["../src/confetti-shader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AASH;;;;GAIG;AACH,eAAO,MAAM,UAAU,MAAM,CAAC;AAE9B,eAAO,MAAM,mBAAmB,oRAO9B,CAAC;AAEH,eAAO,MAAM,qBAAqB,mzGAqEhC,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLSL ES 3.00 source for Confetti (web, PANEL architecture).
|
|
3
|
+
*
|
|
4
|
+
* The paper pieces are rasterized into an offscreen Canvas2D panel each frame
|
|
5
|
+
* (see confetti-renderer.ts) — each piece's pose + lit colour computed ONCE in JS
|
|
6
|
+
* rather than re-derived at every pixel. This shader is now a cheap O(pixels)
|
|
7
|
+
* pass: it SAMPLES that panel and applies only the screen-space finish that wants
|
|
8
|
+
* to be procedural —
|
|
9
|
+
* - the global gain (envelope amp × exposure) + filmic ACES tonemap so dense
|
|
10
|
+
* bursts roll off gracefully on the screen-blend page,
|
|
11
|
+
* - the cel posterize / saturation punch toward the whimsy (cel) end,
|
|
12
|
+
* - an ordered dither to kill screen-blend banding,
|
|
13
|
+
* - and a cheap soft drop-shadow on the multiply pass (a ring-blurred sample of
|
|
14
|
+
* the panel's mass, offset toward the implied light).
|
|
15
|
+
*
|
|
16
|
+
* Why: the old single-pass design looped MAX_PIECES at every fragment
|
|
17
|
+
* (O(pixels × pieces)) which is fine on a GPU but crawls under software/ANGLE
|
|
18
|
+
* WebGL. Sampling a pre-rasterized panel makes the per-pixel cost independent of
|
|
19
|
+
* piece count. Mirrors the comic/heartburst hybrid effects. (The Swift/Metal
|
|
20
|
+
* confetti keeps its analytic GPU pass — this change is web-only; the .dope is
|
|
21
|
+
* unchanged across platforms.)
|
|
22
|
+
*/
|
|
23
|
+
import { GLSL_CONSTANTS, GLSL_DITHER, GLSL_HASH, GLSL_TONEMAP_ACES, } from "@dopaminefx/core";
|
|
24
|
+
/**
|
|
25
|
+
* Max confetti pieces. Single source of truth: BOTH the panel renderer's loop
|
|
26
|
+
* bound and the integer-clamp const the `.dope` mapping references (passed to the
|
|
27
|
+
* loader as `MAX_PIECES`). Counts above this won't render.
|
|
28
|
+
*/
|
|
29
|
+
export const MAX_PIECES = 120;
|
|
30
|
+
export const CONFETTI_VERTEX_SRC = /* glsl */ `#version 300 es
|
|
31
|
+
out vec2 vUv;
|
|
32
|
+
void main() {
|
|
33
|
+
// Single full-screen triangle from gl_VertexID — no vertex buffers needed.
|
|
34
|
+
vec2 pos = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
|
|
35
|
+
vUv = pos;
|
|
36
|
+
gl_Position = vec4(pos * 2.0 - 1.0, 0.0, 1.0);
|
|
37
|
+
}`;
|
|
38
|
+
export const CONFETTI_FRAGMENT_SRC = /* glsl */ `#version 300 es
|
|
39
|
+
precision highp float;
|
|
40
|
+
in vec2 vUv;
|
|
41
|
+
out vec4 fragColor;
|
|
42
|
+
|
|
43
|
+
uniform sampler2D uPanel; // RGB = accumulated per-piece lit colour × fade
|
|
44
|
+
uniform vec2 uResolution; // device pixels
|
|
45
|
+
uniform float uAmp; // envelope amplitude (peaks > 1) — overall brightness
|
|
46
|
+
uniform float uTimeS; // elapsed seconds (snapped "on twos" with style)
|
|
47
|
+
uniform float uExposure;
|
|
48
|
+
uniform float uStyle; // 0..1 photoreal paper -> flat cel shapes
|
|
49
|
+
uniform float uShadow; // 0 = light pass (screen), 1 = shadow pass (multiply)
|
|
50
|
+
uniform vec2 uShadowOffset; // device-px offset of the cast silhouette
|
|
51
|
+
uniform float uShadowSoft; // penumbra softness in device px (blur tap radius)
|
|
52
|
+
uniform float uShadowStrength;// 0..1 max darkening of the multiply layer
|
|
53
|
+
uniform vec3 uC0;
|
|
54
|
+
|
|
55
|
+
${GLSL_CONSTANTS}
|
|
56
|
+
${GLSL_HASH}
|
|
57
|
+
${GLSL_TONEMAP_ACES}
|
|
58
|
+
${GLSL_DITHER}
|
|
59
|
+
|
|
60
|
+
void main() {
|
|
61
|
+
vec2 frag = vUv * uResolution;
|
|
62
|
+
|
|
63
|
+
// ---- SHADOW pass (multiply layer) --------------------------------------
|
|
64
|
+
// A cheap soft drop-shadow: ring-blur the panel's mass at a sample point pushed
|
|
65
|
+
// against the light offset. White = no shadow; darker = cast silhouette.
|
|
66
|
+
if (uShadow > 0.5) {
|
|
67
|
+
vec2 px = 1.0 / uResolution;
|
|
68
|
+
vec2 souv = vUv - uShadowOffset * px;
|
|
69
|
+
float occ = 0.0;
|
|
70
|
+
for (int i = 0; i < 8; i++) {
|
|
71
|
+
float a = float(i) / 8.0 * TAU;
|
|
72
|
+
vec2 o = vec2(cos(a), sin(a)) * uShadowSoft * px;
|
|
73
|
+
vec2 tuv = souv + o;
|
|
74
|
+
vec2 inb = step(vec2(0.0), tuv) * step(tuv, vec2(1.0));
|
|
75
|
+
vec3 s = texture(uPanel, tuv).rgb;
|
|
76
|
+
occ += (s.r + s.g + s.b) * (1.0 / 3.0) * inb.x * inb.y;
|
|
77
|
+
}
|
|
78
|
+
occ /= 8.0;
|
|
79
|
+
float dark = clamp(occ * uAmp, 0.0, 1.0) * uShadowStrength;
|
|
80
|
+
vec3 tint = mix(vec3(1.0), 0.6 + 0.4 * normalize(uC0 + 1e-3), 0.2);
|
|
81
|
+
vec3 mul = mix(vec3(1.0), tint, dark);
|
|
82
|
+
fragColor = vec4(mul, 1.0);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ---- LIGHT pass --------------------------------------------------------
|
|
87
|
+
// The panel already holds Σ(lit × fade) per piece; apply the global gain, then
|
|
88
|
+
// the same filmic + cel + dither finish the original single-pass shader did.
|
|
89
|
+
vec3 col = texture(uPanel, vUv).rgb * (uAmp * uExposure) * 1.35;
|
|
90
|
+
|
|
91
|
+
col = tonemapACES(col * 0.85);
|
|
92
|
+
|
|
93
|
+
// Cel posterize at the whimsy end: punch saturation + quantize into hard bands.
|
|
94
|
+
if (uStyle > 0.001) {
|
|
95
|
+
float l = dot(col, vec3(0.299, 0.587, 0.114));
|
|
96
|
+
vec3 neon = clamp(l + (col - l) * 1.5, 0.0, 1.0);
|
|
97
|
+
vec3 styled = mix(col, neon, 0.65);
|
|
98
|
+
float bands = mix(40.0, 5.0, uStyle);
|
|
99
|
+
styled = floor(styled * bands + 0.5) / bands;
|
|
100
|
+
col = mix(col, styled, uStyle);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Ordered dither to break screen-blend banding; faded toward the cel end.
|
|
104
|
+
col = ditherAdd(col, frag, uTimeS, 1.0 - uStyle);
|
|
105
|
+
|
|
106
|
+
fragColor = vec4(col, 1.0);
|
|
107
|
+
}`;
|
|
108
|
+
//# sourceMappingURL=confetti-shader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confetti-shader.js","sourceRoot":"","sources":["../src/confetti-shader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,cAAc,EACd,WAAW,EACX,SAAS,EACT,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAE1B;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,GAAG,CAAC;AAE9B,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC;;;;;;;EAO5C,CAAC;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;EAiB9C,cAAc;EACd,SAAS;EACT,iBAAiB;EACjB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiDX,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confetti's bespoke timing — the launch-then-fall amplitude envelope.
|
|
3
|
+
*
|
|
4
|
+
* Unlike the success effects' held-breath `envelope` (which decays from its
|
|
5
|
+
* early peak), confetti stays BRIGHT through the long fall — per-piece
|
|
6
|
+
* `particleFade` in the shader handles each piece dimming as it lands. So this
|
|
7
|
+
* is a sharp POP attack (overshoot at launch), a near-full sustain while
|
|
8
|
+
* everything falls, then a gentle fade only at the very end as the last pieces
|
|
9
|
+
* settle. Built on the generic `easeOutBack`/`easeOutCubic` primitives.
|
|
10
|
+
*/
|
|
11
|
+
/** Confetti launch-then-fall amplitude over normalized life. Peak > 1 at launch. */
|
|
12
|
+
export declare function confettiAmp(life: number, overshoot: number): number;
|
|
13
|
+
//# sourceMappingURL=confetti-tempo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confetti-tempo.d.ts","sourceRoot":"","sources":["../src/confetti-tempo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAYnE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confetti's bespoke timing — the launch-then-fall amplitude envelope.
|
|
3
|
+
*
|
|
4
|
+
* Unlike the success effects' held-breath `envelope` (which decays from its
|
|
5
|
+
* early peak), confetti stays BRIGHT through the long fall — per-piece
|
|
6
|
+
* `particleFade` in the shader handles each piece dimming as it lands. So this
|
|
7
|
+
* is a sharp POP attack (overshoot at launch), a near-full sustain while
|
|
8
|
+
* everything falls, then a gentle fade only at the very end as the last pieces
|
|
9
|
+
* settle. Built on the generic `easeOutBack`/`easeOutCubic` primitives.
|
|
10
|
+
*/
|
|
11
|
+
import { easeOutBack, easeOutCubic } from "@dopaminefx/core";
|
|
12
|
+
/** Confetti launch-then-fall amplitude over normalized life. Peak > 1 at launch. */
|
|
13
|
+
export function confettiAmp(life, overshoot) {
|
|
14
|
+
if (life <= 0 || life >= 1)
|
|
15
|
+
return 0;
|
|
16
|
+
const attack = 0.12;
|
|
17
|
+
if (life < attack) {
|
|
18
|
+
// Sharp pop with a little overshoot (the burst leaving the action).
|
|
19
|
+
return easeOutBack(life / attack, overshoot);
|
|
20
|
+
}
|
|
21
|
+
// Long luminous sustain, then a soft fade over the last ~30% as pieces settle.
|
|
22
|
+
const tailStart = 0.7;
|
|
23
|
+
if (life < tailStart)
|
|
24
|
+
return 1;
|
|
25
|
+
const x = (life - tailStart) / (1 - tailStart);
|
|
26
|
+
return 1 - easeOutCubic(x) * 0.85;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=confetti-tempo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confetti-tempo.js","sourceRoot":"","sources":["../src/confetti-tempo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE7D,oFAAoF;AACpF,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,SAAiB;IACzD,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,IAAI,GAAG,MAAM,EAAE,CAAC;QAClB,oEAAoE;QACpE,OAAO,WAAW,CAAC,IAAI,GAAG,MAAM,EAAE,SAAS,CAAC,CAAC;IAC/C,CAAC;IACD,+EAA+E;IAC/E,MAAM,SAAS,GAAG,GAAG,CAAC;IACtB,IAAI,IAAI,GAAG,SAAS;QAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,CAAC,GAAG,CAAC,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;AACpC,CAAC"}
|