@dopaminefx/effect-checkmate 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.
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Checkmate — a fabulous winning move: a chess QUEEN pops into place and the
3
+ * frame erupts in LGBTQ+ pride (rainbow swoosh shockwave + spinning sunburst +
4
+ * twinkling sparkle bling). A success effect (serene / celebratory / electric).
5
+ *
6
+ * FULLY DATA-DRIVEN: params/palette/tempo come from checkmate.dope.json via the
7
+ * loader; the per-frame logic (the held-breath `amp` + the easeOutBack `pop`
8
+ * bounce) is `tempo.frame`; the uniform binding is the `.dope` `binding`
9
+ * contract. `registerDopeEffect` derives the whole pass config from the data —
10
+ * this module is just the rainbow SHADER + the registration call. The chess
11
+ * queen is analytic (2D SDFs), so the effect ships no swift/ or android/ folder.
12
+ */
13
+ import { type EffectFactory, type PassParams } from "@dopaminefx/core";
14
+ /** The resolved render params Checkmate's shader consumes. */
15
+ export interface CheckmateParams extends PassParams {
16
+ exposure: number;
17
+ bling: number;
18
+ swoosh: number;
19
+ rays: number;
20
+ spin: number;
21
+ sizeFrac: number;
22
+ overshoot: number;
23
+ checkmateSeed: number;
24
+ }
25
+ export declare const checkmate: EffectFactory<CheckmateParams>;
26
+ export default checkmate;
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAiC,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAKtG,8DAA8D;AAC9D,MAAM,WAAW,eAAgB,SAAQ,UAAU;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB;AAID,eAAO,MAAM,SAAS,EAGa,aAAa,CAAC,eAAe,CAAC,CAAC;AAElE,eAAe,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Checkmate — a fabulous winning move: a chess QUEEN pops into place and the
3
+ * frame erupts in LGBTQ+ pride (rainbow swoosh shockwave + spinning sunburst +
4
+ * twinkling sparkle bling). A success effect (serene / celebratory / electric).
5
+ *
6
+ * FULLY DATA-DRIVEN: params/palette/tempo come from checkmate.dope.json via the
7
+ * loader; the per-frame logic (the held-breath `amp` + the easeOutBack `pop`
8
+ * bounce) is `tempo.frame`; the uniform binding is the `.dope` `binding`
9
+ * contract. `registerDopeEffect` derives the whole pass config from the data —
10
+ * this module is just the rainbow SHADER + the registration call. The chess
11
+ * queen is analytic (2D SDFs), so the effect ships no swift/ or android/ folder.
12
+ */
13
+ import { CHECKMATE_FRAGMENT_SRC, CHECKMATE_VERTEX_SRC } from "./checkmate-shader.js";
14
+ import { parseDope, registerDopeEffect } from "@dopaminefx/core";
15
+ import doc from "./checkmate.dope.json";
16
+ const DOPE = parseDope(doc);
17
+ // The whole factory (resolve / create / reducedMotion / program registration)
18
+ // is data: checkmate.dope.json interpreted by the core backbone.
19
+ export const checkmate = registerDopeEffect(DOPE, {
20
+ vertex: CHECKMATE_VERTEX_SRC,
21
+ fragment: CHECKMATE_FRAGMENT_SRC,
22
+ });
23
+ export default checkmate;
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AACrF,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAuC,MAAM,kBAAkB,CAAC;AACtG,OAAO,GAAG,MAAM,uBAAuB,CAAC;AAExC,MAAM,IAAI,GAAG,SAAS,CAAC,GAAa,CAAC,CAAC;AActC,8EAA8E;AAC9E,iEAAiE;AACjE,MAAM,CAAC,MAAM,SAAS,GAAG,kBAAkB,CAAC,IAAI,EAAE;IAChD,MAAM,EAAE,oBAAoB;IAC5B,QAAQ,EAAE,sBAAsB;CACjC,CAAgE,CAAC;AAElE,eAAe,SAAS,CAAC"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@dopaminefx/effect-checkmate",
3
+ "version": "0.1.0",
4
+ "description": "Checkmate — a pride-rainbow chess-queen success celebration 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/checkmate/web"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/10in30/dopamine/issues"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ }
46
+ }
@@ -0,0 +1,263 @@
1
+ /**
2
+ * GLSL ES 3.00 source for **Checkmate** — an unapologetically fabulous winning
3
+ * move. A chess QUEEN pops into place with an overshoot bounce and the whole
4
+ * frame ERUPTS in LGBTQ+ pride: an expanding rainbow swoosh-shockwave, a
5
+ * spinning pride sunburst, and a mob of twinkling 4-point sparkle bling.
6
+ *
7
+ * Authored ONCE here; the toolchain transpiles the MSL + Kotlin variants from
8
+ * this single GLSL (`x-build.shader`). The chess queen is drawn ANALYTICALLY
9
+ * from 2D SDF primitives (a flared trapezoid body + base, a collar bar and five
10
+ * crown balls on stems) so it is byte-identical on every backend — no baked SDF,
11
+ * no sampler, no per-platform fallback.
12
+ *
13
+ * Layers, summed as light (canvas is black, `mix-blend-mode: screen`, so black
14
+ * == no change, bright == cast light onto the page beneath):
15
+ * 1. SUNBURST — radial pride rays spinning behind the queen (uSpin/uRays).
16
+ * 2. SWOOSH — an expanding rainbow shockwave ring whose hue cycles with
17
+ * angle; it bursts outward over life (the "swoosh").
18
+ * 3. QUEEN — the chess piece, filled with a vertical rainbow gradient +
19
+ * a hot white edge, popped in by uPop (overshoot bounce).
20
+ * 4. BLING — a scatter of twinkling 4-point star glints riding outward
21
+ * with the swoosh (the sparkle "bling"), tinted by the palette.
22
+ * 5. FLASH — a hot radial flash at the instant of the pop.
23
+ *
24
+ * whimsy == uStyle: 0 = smooth photoreal spectral glow; 1 = cel POP-ART — the
25
+ * rainbow posterizes into the canonical 6-stripe pride flag and the bling reads
26
+ * as chunky comic stars. The pass runner already snaps the clock "on twos".
27
+ */
28
+
29
+ import {
30
+ GLSL_CONSTANTS,
31
+ GLSL_DITHER,
32
+ GLSL_HASH,
33
+ GLSL_PALETTE_MIX,
34
+ GLSL_SD_SEG,
35
+ GLSL_TONEMAP_ACES,
36
+ } from "@dopaminefx/core";
37
+
38
+ /** Scatter count for the sparkle bling. Single source of truth for the loop cap. */
39
+ export const MAX_SPARKLES = 16;
40
+
41
+ export const CHECKMATE_VERTEX_SRC = /* glsl */ `#version 300 es
42
+ void main() {
43
+ // Single full-screen triangle from gl_VertexID — no vertex buffers needed.
44
+ vec2 pos = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
45
+ gl_Position = vec4(pos * 2.0 - 1.0, 0.0, 1.0);
46
+ }`;
47
+
48
+ export const CHECKMATE_FRAGMENT_SRC = /* glsl */ `#version 300 es
49
+ precision highp float;
50
+ out vec4 fragColor;
51
+
52
+ uniform vec2 uResolution; // device pixels
53
+ uniform vec2 uOrigin; // queen anchor, gl coords (y up)
54
+ uniform float uAmp; // held-breath envelope amplitude (brightness gate)
55
+ uniform float uPop; // easeOutBack pop scale (overshoot bounce -> 1)
56
+ uniform float uLife; // whole-effect progress 0..1
57
+ uniform float uTimeS; // elapsed seconds (snapped "on twos" by style)
58
+ uniform float uExposure; // overall light gain (intensity)
59
+ uniform float uBling; // sparkle density/brightness (intensity)
60
+ uniform float uSwoosh; // rainbow shockwave reach (intensity)
61
+ uniform float uRays; // pride sunburst ray count (integer)
62
+ uniform float uSpin; // sunburst/swoosh rotation speed
63
+ uniform float uSizeFrac; // queen box size as a fraction of min viewport dim
64
+ uniform float uSeed; // per-fire scatter/hue offset
65
+ uniform float uStyle; // 0..1 photoreal spectral -> cel pop-art pride flag
66
+ uniform float uShadow; // 0 = light pass (screen), 1 = shadow pass (multiply)
67
+ uniform vec2 uShadowOffset; // device-px offset of the cast silhouette
68
+ uniform float uShadowSoft; // penumbra softness in device px (blur tap radius)
69
+ uniform float uShadowStrength;// 0..1 max darkening of the multiply layer
70
+ uniform vec3 uC0; // accent palette (sparkle tint) — per fire
71
+ uniform vec3 uC1; // mid
72
+ uniform vec3 uC2; // outer accent
73
+
74
+ #define MAX_SPARKLES ${MAX_SPARKLES}
75
+ ${GLSL_CONSTANTS}
76
+ ${GLSL_HASH}
77
+ ${GLSL_PALETTE_MIX}
78
+ ${GLSL_SD_SEG}
79
+ ${GLSL_TONEMAP_ACES}
80
+ ${GLSL_DITHER}
81
+
82
+ // ---- The pride spectrum -----------------------------------------------------
83
+ // Smooth IQ-cosine rainbow: a continuous, saturated spectral sweep over [0,1).
84
+ vec3 prideSmooth(float t){
85
+ t = fract(t);
86
+ return 0.5 + 0.5 * cos(TAU * (t + vec3(0.0, 0.33, 0.67)));
87
+ }
88
+ // The canonical 6-stripe pride FLAG, posterized from t (red→violet).
89
+ vec3 prideFlag(float t){
90
+ t = fract(t);
91
+ if (t < 0.16667) return vec3(0.94, 0.10, 0.12); // red
92
+ if (t < 0.33333) return vec3(1.00, 0.55, 0.06); // orange
93
+ if (t < 0.50000) return vec3(1.00, 0.93, 0.10); // yellow
94
+ if (t < 0.66667) return vec3(0.18, 0.70, 0.22); // green
95
+ if (t < 0.83333) return vec3(0.10, 0.36, 0.90); // blue
96
+ return vec3(0.46, 0.12, 0.62); // violet
97
+ }
98
+ // Blend smooth↔flag by whimsy (style): cel end snaps to the 6 flag stripes.
99
+ vec3 prideColor(float t, float style){
100
+ return mix(prideSmooth(t), prideFlag(t), style);
101
+ }
102
+
103
+ // ---- 2D SDF primitives for the chess QUEEN silhouette -----------------------
104
+ float sdBox(vec2 p, vec2 b){
105
+ vec2 d = abs(p) - b;
106
+ return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
107
+ }
108
+ // Inigo Quilez trapezoid SDF: half-widths r1 (bottom) and r2 (top), half-height he.
109
+ float sdTrapezoid(vec2 p, float r1, float r2, float he){
110
+ vec2 k1 = vec2(r2, he);
111
+ vec2 k2 = vec2(r2 - r1, 2.0 * he);
112
+ p.x = abs(p.x);
113
+ vec2 ca = vec2(p.x - min(p.x, (p.y < 0.0) ? r1 : r2), abs(p.y) - he);
114
+ vec2 cb = p - k1 + k2 * clamp(dot(k1 - p, k2) / dot(k2, k2), 0.0, 1.0);
115
+ float s = (cb.x < 0.0 && ca.y < 0.0) ? -1.0 : 1.0;
116
+ return s * sqrt(min(dot(ca, ca), dot(cb, cb)));
117
+ }
118
+ // Signed distance to the queen, in LOCAL units (q centered, y up, ~[-1,1]).
119
+ // Union (min) of: base foot, flared body, collar band, five crown balls + stems.
120
+ float queenDist(vec2 q){
121
+ float d = sdTrapezoid(q - vec2(0.0, -0.74), 0.60, 0.40, 0.12); // base foot
122
+ d = min(d, sdTrapezoid(q - vec2(0.0, -0.10), 0.46, 0.15, 0.50)); // flared body
123
+ d = min(d, sdBox(q - vec2(0.0, 0.40), vec2(0.32, 0.05)) - 0.02); // collar band
124
+ // crown balls (center tallest) + the stems joining them to the band
125
+ d = min(d, length(q - vec2(-0.46, 0.55)) - 0.115);
126
+ d = min(d, length(q - vec2(-0.23, 0.62)) - 0.125);
127
+ d = min(d, length(q - vec2( 0.00, 0.71)) - 0.145);
128
+ d = min(d, length(q - vec2( 0.23, 0.62)) - 0.125);
129
+ d = min(d, length(q - vec2( 0.46, 0.55)) - 0.115);
130
+ d = min(d, sdSeg(q, vec2(-0.46, 0.55), vec2(-0.28, 0.42)) - 0.045);
131
+ d = min(d, sdSeg(q, vec2(-0.23, 0.62), vec2(-0.14, 0.42)) - 0.045);
132
+ d = min(d, sdSeg(q, vec2( 0.00, 0.71), vec2( 0.00, 0.42)) - 0.055);
133
+ d = min(d, sdSeg(q, vec2( 0.23, 0.62), vec2( 0.14, 0.42)) - 0.045);
134
+ d = min(d, sdSeg(q, vec2( 0.46, 0.55), vec2( 0.28, 0.42)) - 0.045);
135
+ return d;
136
+ }
137
+
138
+ // A twinkling 4-point star glint: hot core + two soft anisotropic spikes.
139
+ float starGlint(vec2 p, vec2 c, float size){
140
+ vec2 d = (p - c) / max(size, 1e-3);
141
+ float r = length(d);
142
+ float core = exp(-r * r * 5.0);
143
+ float sx = exp(-abs(d.x) * 6.0) * exp(-abs(d.y) * 1.4);
144
+ float sy = exp(-abs(d.y) * 6.0) * exp(-abs(d.x) * 1.4);
145
+ return core + (sx + sy) * 0.7;
146
+ }
147
+
148
+ // ---- Queen coverage (0..1 fill) at a fragment, with the pop scale applied ----
149
+ float queenFill(vec2 frag){
150
+ float R = min(uResolution.x, uResolution.y) * uSizeFrac;
151
+ float scale = mix(0.34, 1.0, clamp(uPop, 0.0, 1.4)); // bounce-in scale
152
+ vec2 q = (frag - uOrigin) / max(R, 1e-3) / max(scale, 1e-3);
153
+ float d = queenDist(q);
154
+ float aa = 1.6 / max(R, 1e-3); // ~1.6 device px, local units
155
+ return smoothstep(aa, -aa, d);
156
+ }
157
+
158
+ // ---- SHADOW silhouette — the queen casts a soft offset occlusion. -----------
159
+ float occlusion(vec2 frag){
160
+ return clamp(queenFill(frag) * uAmp, 0.0, 1.0);
161
+ }
162
+ vec4 shadowColor(vec2 frag){
163
+ vec2 sp = frag - uShadowOffset;
164
+ float s = uShadowSoft;
165
+ float occ = occlusion(sp);
166
+ occ += occlusion(sp + vec2(s, 0.0));
167
+ occ += occlusion(sp + vec2(-s, 0.0));
168
+ occ += occlusion(sp + vec2(0.0, s));
169
+ occ += occlusion(sp + vec2(0.0, -s));
170
+ occ /= 5.0;
171
+ float dark = clamp(occ, 0.0, 1.0) * uShadowStrength;
172
+ // A faintly warm, regal shadow tint (not a flat grey).
173
+ vec3 tint = mix(vec3(1.0), vec3(0.74, 0.70, 0.78), 1.0);
174
+ vec3 mul = mix(vec3(1.0), tint, dark);
175
+ return vec4(mul, 1.0);
176
+ }
177
+
178
+ void main(){
179
+ vec2 frag = gl_FragCoord.xy;
180
+ float minDim = min(uResolution.x, uResolution.y);
181
+
182
+ if (uShadow > 0.5) { fragColor = shadowColor(frag); return; }
183
+
184
+ vec2 rel = frag - uOrigin;
185
+ float r = length(rel);
186
+ float rn = r / minDim; // normalized radius
187
+ float theta = atan(rel.y, rel.x); // -PI..PI
188
+ float gain = uAmp * uExposure;
189
+ float style = uStyle;
190
+
191
+ vec3 col = vec3(0.0);
192
+
193
+ // ---- 1. PRIDE SUNBURST: spinning radial rays behind the queen. ----
194
+ float rayN = max(uRays, 1.0);
195
+ float rays = 0.5 + 0.5 * cos(theta * rayN - uTimeS * uSpin * 2.4);
196
+ rays = pow(clamp(rays, 0.0, 1.0), mix(2.2, 5.0, style)); // crisper on the cel end
197
+ float rayMask = smoothstep(0.02, 0.16, rn) * (1.0 - smoothstep(0.30, 0.62, rn));
198
+ vec3 rayCol = prideColor(theta / TAU + 0.5 + uSeed, style);
199
+ col += rayCol * rays * rayMask * gain * 0.5;
200
+
201
+ // ---- 2. RAINBOW SWOOSH: an expanding shockwave ring whose hue cycles with
202
+ // angle. It bursts outward over life and widens as it goes — the "swoosh". ----
203
+ float front = uSwoosh * (0.10 + 0.78 * uLife);
204
+ float width = 0.035 + 0.16 * uLife;
205
+ float dr = (rn - front) / max(width, 1e-3);
206
+ float ring = exp(-dr * dr);
207
+ vec3 ringCol = prideColor(theta / TAU + uSpin * uTimeS * 0.15 + uSeed, style);
208
+ col += ringCol * ring * gain * 1.35;
209
+ // a brighter leading lip on the ring's outer edge (the wet swoosh shine)
210
+ col += vec3(1.0) * smoothstep(0.0, 1.0, ring) * smoothstep(0.0, -1.2, dr) * gain * 0.35;
211
+
212
+ // ---- 3. THE QUEEN: filled with a vertical rainbow + a hot white edge. ----
213
+ float R = minDim * uSizeFrac;
214
+ float scale = mix(0.34, 1.0, clamp(uPop, 0.0, 1.4));
215
+ vec2 q = rel / max(R, 1e-3) / max(scale, 1e-3);
216
+ float dq = queenDist(q);
217
+ float aa = 1.6 / max(R, 1e-3);
218
+ float fill = smoothstep(aa, -aa, dq);
219
+ float edge = smoothstep(aa * 2.5, 0.0, abs(dq)); // bright rim line
220
+ float halo = exp(-max(dq, 0.0) / 0.06); // soft outer glow
221
+ // vertical rainbow over the piece + a slow shimmer; whimsy → flag stripes.
222
+ float qt = q.y * 0.42 + 0.5 + uSeed + uTimeS * 0.05;
223
+ vec3 body = prideColor(qt, style);
224
+ vec3 queenCol = body * fill * 1.45 // saturated rainbow fill
225
+ + mix(body, vec3(1.0), 0.6) * edge * 0.8 // bright (tinted) edge
226
+ + body * halo * 0.55; // coloured glow
227
+ // a brief white core just after the bounce so she "lands" with a flash
228
+ queenCol += vec3(1.0) * fill * (1.0 - smoothstep(0.0, 0.22, uLife)) * 0.35;
229
+ col += queenCol * (uExposure * (0.35 + 0.65 * uAmp));
230
+
231
+ // ---- 4. SPARKLE BLING: twinkling 4-point stars riding outward. ----
232
+ float sparkleReach = (0.16 + 0.62 * uLife) * minDim;
233
+ vec3 bling = vec3(0.0);
234
+ for (int i = 0; i < MAX_SPARKLES; i++) {
235
+ float fi = float(i);
236
+ vec2 h = hash21(fi * 3.17 + uSeed * 31.0);
237
+ float ang = h.x * TAU;
238
+ float rad = (0.45 + 0.55 * h.y) * sparkleReach; // spread out as life grows
239
+ vec2 pos = uOrigin + vec2(cos(ang), sin(ang)) * rad;
240
+ // twinkle: each sparkle blinks on its own phase
241
+ float ph = h.x * 17.0 + h.y * 9.0;
242
+ float tw = pow(0.5 + 0.5 * sin(uTimeS * 7.0 + ph), mix(3.0, 7.0, style));
243
+ float sz = minDim * (0.012 + 0.018 * h.y) * (0.8 + 0.5 * uBling);
244
+ float g = starGlint(frag, pos, sz) * tw;
245
+ // tint with the per-fire accent palette, biased bright (white-hot core).
246
+ vec3 tint = mix(vec3(1.0), paletteMix(fract(fi * 0.137 + uSeed)), 0.55);
247
+ bling += tint * g;
248
+ }
249
+ col += bling * uBling * gain * 0.9;
250
+
251
+ // ---- 5. POP FLASH: a hot radial flash at the instant of the bounce. ----
252
+ float flashT = 1.0 - smoothstep(0.0, 0.22, uLife);
253
+ float flash = exp(-rn * rn * 26.0) * flashT;
254
+ col += mix(vec3(1.0), prideColor(uTimeS * 0.3 + uSeed, style), 0.4) * flash * uExposure * 1.4;
255
+
256
+ // ---- Filmic tonemap + finishing ----
257
+ col = tonemapACES(col * 0.95);
258
+ // Ordered dither (~1/255) to kill banding the screen blend reveals; faded out
259
+ // toward the cel/pop-art end where hard flag bands are intended.
260
+ col = ditherAdd(col, frag, uTimeS, 1.0 - style);
261
+
262
+ fragColor = vec4(max(col, 0.0), 1.0);
263
+ }`;