|
| 1 | +/** |
| 2 | + * ADHD Hyperfocus — WebGL Effect |
| 3 | + * Tunnel-vision center clarity, peripheral fade, dopamine sparks, paralysis slider. |
| 4 | + */ |
| 5 | + |
| 6 | +import * as THREE from 'three' |
| 7 | +import { initializeEffect } from '../../core' |
| 8 | +import { ComboboxControl, Effect, NumberControl } from '../../core/controls/decorators' |
| 9 | +import { comboboxValueToIndex, normalizePercentage } from '../../core/controls/helpers' |
| 10 | +import { WebGLEffect } from '../../core/effects/webgl-effect' |
| 11 | + |
| 12 | +import fragmentShader from './fragment.glsl' |
| 13 | + |
| 14 | +declare global { |
| 15 | + interface Window { |
| 16 | + focusRadius: number |
| 17 | + focusStrength: number |
| 18 | + peripheralBlur: number |
| 19 | + saturation: number |
| 20 | + energy: number |
| 21 | + sparkDensity: number |
| 22 | + tunnelSpeed: number |
| 23 | + paralysis: number |
| 24 | + noise: number |
| 25 | + colorMode: string | number |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +export interface ADHDHyperfocusControls { |
| 30 | + focusRadius: number // 0-1 |
| 31 | + focusStrength: number // 0-2 |
| 32 | + peripheralBlur: number // 0-2 |
| 33 | + saturation: number // 0-2 |
| 34 | + energy: number // 0-2 |
| 35 | + sparkDensity: number // 0-2 |
| 36 | + tunnelSpeed: number // 0-2 |
| 37 | + paralysis: number // 0-1 |
| 38 | + noise: number // 0-2 |
| 39 | + colorMode: number // 0..2 |
| 40 | +} |
| 41 | + |
| 42 | +@Effect({ |
| 43 | + author: 'hyperb1iss', |
| 44 | + description: 'Tunnel vision with hyperfocused center and dopamine sparks. Peripheral fade, paralysis control.', |
| 45 | + name: 'ADHD Hyperfocus', |
| 46 | +}) |
| 47 | +export class ADHDHyperfocusEffect extends WebGLEffect<ADHDHyperfocusControls> { |
| 48 | + private readonly colorModes = ['Dopamine', 'Neon', 'Mono'] |
| 49 | + |
| 50 | + @NumberControl({ |
| 51 | + default: 28, |
| 52 | + label: 'Focus Radius', |
| 53 | + max: 100, |
| 54 | + min: 5, |
| 55 | + tooltip: 'Radius of the sharp center region', |
| 56 | + }) |
| 57 | + focusRadius!: number |
| 58 | + |
| 59 | + @NumberControl({ |
| 60 | + default: 120, |
| 61 | + label: 'Focus Strength', |
| 62 | + max: 200, |
| 63 | + min: 0, |
| 64 | + tooltip: 'Center boost/magnification', |
| 65 | + }) |
| 66 | + focusStrength!: number |
| 67 | + |
| 68 | + @NumberControl({ |
| 69 | + default: 60, |
| 70 | + label: 'Peripheral Blur', |
| 71 | + max: 200, |
| 72 | + min: 0, |
| 73 | + tooltip: 'How much the periphery fades/softens', |
| 74 | + }) |
| 75 | + peripheralBlur!: number |
| 76 | + |
| 77 | + @NumberControl({ |
| 78 | + default: 120, |
| 79 | + label: 'Saturation', |
| 80 | + max: 200, |
| 81 | + min: 0, |
| 82 | + tooltip: 'Color saturation', |
| 83 | + }) |
| 84 | + saturation!: number |
| 85 | + |
| 86 | + @NumberControl({ |
| 87 | + default: 120, |
| 88 | + label: 'Energy (Brightness)', |
| 89 | + max: 200, |
| 90 | + min: 10, |
| 91 | + tooltip: 'Brightness/energy of the effect', |
| 92 | + }) |
| 93 | + energy!: number |
| 94 | + |
| 95 | + @NumberControl({ |
| 96 | + default: 80, |
| 97 | + label: 'Spark Density', |
| 98 | + max: 200, |
| 99 | + min: 0, |
| 100 | + tooltip: 'Amount of dopamine sparks', |
| 101 | + }) |
| 102 | + sparkDensity!: number |
| 103 | + |
| 104 | + @NumberControl({ |
| 105 | + default: 70, |
| 106 | + label: 'Tunnel Speed', |
| 107 | + max: 200, |
| 108 | + min: 0, |
| 109 | + tooltip: 'Motion speed of the tunnel rings', |
| 110 | + }) |
| 111 | + tunnelSpeed!: number |
| 112 | + |
| 113 | + @NumberControl({ |
| 114 | + default: 25, |
| 115 | + label: 'Paralysis', |
| 116 | + max: 100, |
| 117 | + min: 0, |
| 118 | + tooltip: 'Executive dysfunction; reduces motion and spark speed', |
| 119 | + }) |
| 120 | + paralysis!: number |
| 121 | + |
| 122 | + @NumberControl({ |
| 123 | + default: 40, |
| 124 | + label: 'Noise', |
| 125 | + max: 200, |
| 126 | + min: 0, |
| 127 | + tooltip: 'Film/noise amount, stronger in periphery', |
| 128 | + }) |
| 129 | + noise!: number |
| 130 | + |
| 131 | + @ComboboxControl({ |
| 132 | + default: 'Dopamine', |
| 133 | + label: 'Color Mode', |
| 134 | + tooltip: 'Dopamine (warm/cool), Neon, or Mono', |
| 135 | + values: ['Dopamine', 'Neon', 'Mono'], |
| 136 | + }) |
| 137 | + colorMode!: string |
| 138 | + |
| 139 | + constructor() { |
| 140 | + super({ |
| 141 | + debug: true, |
| 142 | + fragmentShader, |
| 143 | + id: 'adhd-hyperfocus', |
| 144 | + name: 'ADHD Hyperfocus', |
| 145 | + }) |
| 146 | + } |
| 147 | + |
| 148 | + protected initializeControls(): void { |
| 149 | + window.focusRadius = 28 |
| 150 | + window.focusStrength = 120 |
| 151 | + window.peripheralBlur = 60 |
| 152 | + window.saturation = 120 |
| 153 | + window.energy = 120 |
| 154 | + window.sparkDensity = 80 |
| 155 | + window.tunnelSpeed = 70 |
| 156 | + window.paralysis = 25 |
| 157 | + window.noise = 40 |
| 158 | + window.colorMode = 'Dopamine' |
| 159 | + } |
| 160 | + |
| 161 | + protected getControlValues(): ADHDHyperfocusControls { |
| 162 | + const colorModeIndex = comboboxValueToIndex(window.colorMode ?? 'Dopamine', this.colorModes, 0) |
| 163 | + return { |
| 164 | + colorMode: colorModeIndex, |
| 165 | + energy: normalizePercentage(window.energy ?? 120, 120, 0.1) * 2.0, |
| 166 | + focusRadius: normalizePercentage(window.focusRadius ?? 28, 100, 0.05), |
| 167 | + focusStrength: normalizePercentage(window.focusStrength ?? 120, 100, 0.0) * 2.0, |
| 168 | + noise: normalizePercentage(window.noise ?? 40, 100, 0.0) * 2.0, |
| 169 | + paralysis: normalizePercentage(window.paralysis ?? 25, 100, 0.0), |
| 170 | + peripheralBlur: normalizePercentage(window.peripheralBlur ?? 60, 100, 0.0) * 2.0, |
| 171 | + saturation: normalizePercentage(window.saturation ?? 120, 100, 0.0) * 2.0, |
| 172 | + sparkDensity: normalizePercentage(window.sparkDensity ?? 80, 100, 0.0) * 2.0, |
| 173 | + tunnelSpeed: normalizePercentage(window.tunnelSpeed ?? 70, 100, 0.0) * 2.0, |
| 174 | + } |
| 175 | + } |
| 176 | + |
| 177 | + protected createUniforms(): Record<string, THREE.IUniform> { |
| 178 | + return { |
| 179 | + iColorMode: { value: 0 }, |
| 180 | + iEnergy: { value: 1.2 }, |
| 181 | + iFocusRadius: { value: 0.28 }, |
| 182 | + iFocusStrength: { value: 1.2 }, |
| 183 | + iNoise: { value: 0.8 }, |
| 184 | + iParalysis: { value: 0.25 }, |
| 185 | + iPeripheralBlur: { value: 1.2 }, |
| 186 | + iSaturation: { value: 1.2 }, |
| 187 | + iSparkDensity: { value: 1.0 }, |
| 188 | + iTunnelSpeed: { value: 1.0 }, |
| 189 | + } |
| 190 | + } |
| 191 | + |
| 192 | + protected updateUniforms(c: ADHDHyperfocusControls): void { |
| 193 | + if (!this.material) return |
| 194 | + this.material.uniforms.iFocusRadius.value = c.focusRadius |
| 195 | + this.material.uniforms.iFocusStrength.value = c.focusStrength |
| 196 | + this.material.uniforms.iPeripheralBlur.value = c.peripheralBlur |
| 197 | + this.material.uniforms.iSaturation.value = c.saturation |
| 198 | + this.material.uniforms.iEnergy.value = c.energy |
| 199 | + this.material.uniforms.iSparkDensity.value = c.sparkDensity |
| 200 | + this.material.uniforms.iTunnelSpeed.value = c.tunnelSpeed |
| 201 | + this.material.uniforms.iParalysis.value = c.paralysis |
| 202 | + this.material.uniforms.iNoise.value = c.noise |
| 203 | + this.material.uniforms.iColorMode.value = c.colorMode |
| 204 | + } |
| 205 | +} |
| 206 | + |
| 207 | +const effect = new ADHDHyperfocusEffect() |
| 208 | +initializeEffect(() => effect.initialize()) |
| 209 | +export default effect |
0 commit comments