reel-deal 0.1.0 → 0.1.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/defaults.ts","../src/utils/math.ts","../src/normalize.ts","../src/utils/dom.ts","../src/ReelDeal.ts"],"sourcesContent":["import type { ReelDealIdleBob, ReelDealSpinBlur, ReelDealSpinConfig } from './types';\n\nexport const defaultSpinConfig: ReelDealSpinConfig = {\n minSpins: 2,\n durationMs: 4000,\n speedPxS: 4000,\n staggerMs: 300,\n};\n\nexport const defaultSpinBlur: ReelDealSpinBlur = {\n maxPx: 4,\n speedAtMax: 2200,\n};\n\nexport const defaultIdleBob: ReelDealIdleBob = {\n amplitudePx: 9,\n speedHz: 0.25,\n phaseOffsetRad: 0.9,\n rampMs: 800,\n};\n","const SPIN_ACCEL_END = 0.1;\nconst SPIN_DECEL_START = 0.6;\n\nexport const clampInt = (value: number, min: number, max: number): number =>\n Math.max(min, Math.min(max, Math.floor(value)));\n\nexport const mod = (n: number, m: number): number => ((n % m) + m) % m;\n\nexport const clamp01 = (t: number): number => Math.max(0, Math.min(1, t));\n\nexport const rampHermite = (u: number): number => {\n const x = clamp01(u);\n\n return x ** 2 * (2 - x);\n};\n\nexport const brakeHermite = (u: number): number => {\n const x = clamp01(u);\n\n return x + x ** 2 - x ** 3;\n};\n\nexport const reelProgress = (t: number): number => {\n const tt = clamp01(t);\n\n if (tt <= SPIN_ACCEL_END) {\n const u = tt / SPIN_ACCEL_END;\n\n return rampHermite(u) * SPIN_ACCEL_END;\n }\n\n if (tt < SPIN_DECEL_START) {\n return tt;\n }\n\n const u = (tt - SPIN_DECEL_START) / (1 - SPIN_DECEL_START);\n\n return SPIN_DECEL_START + brakeHermite(u) * (1 - SPIN_DECEL_START);\n};\n\nexport const finiteOr = (value: number | undefined, fallback: number): number =>\n typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n\nexport const nonNegative = (value: number | undefined, fallback: number): number =>\n Math.max(0, finiteOr(value, fallback));\n\nexport const nonNegativeInt = (value: number | undefined, fallback: number): number =>\n Math.max(0, Math.floor(finiteOr(value, fallback)));\n","import { defaultIdleBob, defaultSpinBlur, defaultSpinConfig } from './defaults';\nimport type { ReelDealIdleBob, ReelDealSpinBlur, ReelDealSpinConfig } from './types';\nimport { finiteOr, nonNegative, nonNegativeInt } from './utils/math';\n\nconst DEFAULT_MAX_DPR = 2;\n\nexport type NormalizedSpinConfig = Required<ReelDealSpinConfig>;\n\nexport const normalizeSpinConfig = (\n overrides?: Partial<ReelDealSpinConfig>,\n): NormalizedSpinConfig => {\n const base = { ...defaultSpinConfig, ...(overrides ?? {}) };\n\n return {\n minSpins: nonNegativeInt(base.minSpins, defaultSpinConfig.minSpins),\n durationMs: nonNegativeInt(base.durationMs, defaultSpinConfig.durationMs ?? 0),\n speedPxS: nonNegativeInt(base.speedPxS, defaultSpinConfig.speedPxS ?? 0),\n staggerMs: nonNegativeInt(base.staggerMs, defaultSpinConfig.staggerMs ?? 0),\n };\n};\n\nexport const normalizeSpinBlur = (overrides?: Partial<ReelDealSpinBlur>): ReelDealSpinBlur => {\n const base = { ...defaultSpinBlur, ...(overrides ?? {}) };\n\n return {\n maxPx: nonNegative(base.maxPx, defaultSpinBlur.maxPx),\n speedAtMax: nonNegative(base.speedAtMax, defaultSpinBlur.speedAtMax),\n };\n};\n\nexport const normalizeIdleBob = (\n overrides?: Partial<ReelDealIdleBob> | false,\n): ReelDealIdleBob | null => {\n if (overrides === false) return null;\n\n const base = { ...defaultIdleBob, ...(overrides ?? {}) };\n\n return {\n amplitudePx: nonNegative(base.amplitudePx, defaultIdleBob.amplitudePx),\n speedHz: nonNegative(base.speedHz, defaultIdleBob.speedHz),\n phaseOffsetRad: finiteOr(base.phaseOffsetRad, defaultIdleBob.phaseOffsetRad),\n rampMs: nonNegativeInt(base.rampMs, defaultIdleBob.rampMs),\n };\n};\n\nexport const normalizeMaxDpr = (value?: number): number =>\n Math.max(1, finiteOr(value, DEFAULT_MAX_DPR));\n","export const resolveElementById = <T extends HTMLElement>(id: string, kind: string): T => {\n const el = document.getElementById(id);\n\n if (!el) {\n throw new Error(`${kind} not found: ${id}`);\n }\n\n return el as T;\n};\n\nexport const resolveElement = <T extends HTMLElement>(elOrId: T | string, kind: string): T => {\n if (typeof elOrId === 'string') return resolveElementById<T>(elOrId, kind);\n\n return elOrId;\n};\n","import {\n type NormalizedSpinConfig,\n normalizeIdleBob,\n normalizeMaxDpr,\n normalizeSpinBlur,\n normalizeSpinConfig,\n} from './normalize';\nimport type {\n ReelDealIdleBob,\n ReelDealOptions,\n ReelDealSpinBlur,\n ReelDealSpinConfig,\n ReelDealSpinState,\n} from './types';\nimport { resolveElement } from './utils/dom';\nimport { clamp01, clampInt, mod, nonNegative, rampHermite, reelProgress } from './utils/math';\n\nconst MAX_REELS = 5;\nconst SPIN_TICK_START = 0.72;\nconst SPIN_TICK_RANGE = 0.28;\nconst SPIN_TICK_MAX_PX = 7;\nconst VELOCITY_DECAY = 0.85;\nconst VELOCITY_GAIN = 1 - VELOCITY_DECAY;\nconst STOP_SPRING_OVERSHOOT_PX = 30;\nconst STOP_SPRING_SETTLE_MS = 500;\nconst STOP_SPRING_OSCILLATIONS = 1.25;\nconst STOP_SPRING_DAMPING = 4;\nconst STOP_SPRING_MAX_RATIO = 0.25;\n\ntype WebGLShadingConfig = {\n warpAngleDeg: number;\n edgePower: number;\n};\n\nconst DEFAULT_WEBGL_SHADING: WebGLShadingConfig = {\n warpAngleDeg: 360,\n edgePower: 0.5,\n};\n\nconst normalizeWebGLShading = (overrides?: Partial<WebGLShadingConfig>): WebGLShadingConfig => ({\n warpAngleDeg: nonNegative(overrides?.warpAngleDeg, DEFAULT_WEBGL_SHADING.warpAngleDeg),\n edgePower: nonNegative(overrides?.edgePower, DEFAULT_WEBGL_SHADING.edgePower),\n});\n\ntype SpinAnimation = {\n startTime: number;\n startOffsets: number[];\n deltas: number[];\n durationMs: number[];\n settleDurationMs: number[];\n targets: number[];\n};\n\nexport class ReelDeal {\n private readonly canvas: HTMLCanvasElement;\n private readonly container: HTMLElement;\n\n private readonly opts: ReelDealOptions;\n private readonly visibleSlots = 3;\n private readonly heightScale = 2 / 3;\n private readonly centerIndex = 1;\n private readonly reels: number;\n private readonly spinConfig: NormalizedSpinConfig;\n private readonly spinBlur: ReelDealSpinBlur;\n private readonly webglShading: WebGLShadingConfig;\n private readonly idleBob: ReelDealIdleBob | null;\n private readonly maxDpr: number;\n\n private spriteImg: HTMLImageElement | null = null;\n private spriteWidth = 0;\n private spriteHeight = 0;\n private frameHeight = 0;\n\n private width = 0;\n private height = 0;\n private dpr = 1;\n\n private offsets: number[];\n private velocities: number[];\n private lastVelT: number | null = null;\n private lastVelOffsets: number[];\n private idleStartTime: number | null = null;\n private idleBaseOffsets: number[];\n\n private gl: WebGLRenderingContext | WebGL2RenderingContext | null = null;\n private glProgram: WebGLProgram | null = null;\n private glBuffer: WebGLBuffer | null = null;\n private glTexture: WebGLTexture | null = null;\n private glAttribs: { pos: number; uv: number } | null = null;\n private webglInitError: string | null = null;\n private glUniforms: {\n texture: WebGLUniformLocation | null;\n reelX: WebGLUniformLocation | null;\n reelW: WebGLUniformLocation | null;\n offsetPx: WebGLUniformLocation | null;\n frameHeight: WebGLUniformLocation | null;\n spriteHeight: WebGLUniformLocation | null;\n visibleSlots: WebGLUniformLocation | null;\n heightScale: WebGLUniformLocation | null;\n edgePower: WebGLUniformLocation | null;\n warpAngle: WebGLUniformLocation | null;\n blurPx: WebGLUniformLocation | null;\n } | null = null;\n\n private anim: SpinAnimation | null = null;\n private rafId: number | null = null;\n private pendingSpinResolvers: Array<() => void> = [];\n\n private resizeObserver: ResizeObserver | null = null;\n private resizeQueued = false;\n\n private button: HTMLButtonElement | null = null;\n private buttonHandlerAttached = false;\n private spinning = false;\n private queuedSpins: ReelDealSpinState[] | null = null;\n\n constructor(options: ReelDealOptions) {\n this.opts = options;\n this.reels = clampInt(options.reels ?? 1, 1, MAX_REELS);\n this.spinConfig = normalizeSpinConfig(options.spinConfig);\n this.spinBlur = normalizeSpinBlur(options.spinBlur);\n this.webglShading = normalizeWebGLShading(\n (options as { webglShading?: Partial<WebGLShadingConfig> }).webglShading,\n );\n this.idleBob = normalizeIdleBob(options.idleBob);\n this.maxDpr = normalizeMaxDpr(options.maxDpr);\n\n this.canvas = resolveElement<HTMLCanvasElement>(options.canvas, 'Canvas');\n this.container = resolveElement<HTMLElement>(options.container, 'Container');\n\n this.offsets = new Array(this.reels).fill(0);\n this.velocities = new Array(this.reels).fill(0);\n this.lastVelOffsets = new Array(this.reels).fill(0);\n this.idleBaseOffsets = new Array(this.reels).fill(0);\n }\n\n async init(): Promise<void> {\n await this.loadSprite();\n\n this.tryInitWebGL();\n\n if (!this.gl) {\n throw new Error(\n `WebGL is not supported or failed to initialize. ${this.webglInitError ?? ''}`.trim(),\n );\n }\n\n this.syncContainerLayoutVars();\n this.ensureCanvasCoversContainer();\n this.setupResizeWatcher();\n this.resizeToContainer();\n\n this.setSegments(this.opts.initialSegments);\n\n this.button = this.resolveButton();\n this.queuedSpins = this.resolveSpinQueue();\n this.attachButtonHandler();\n\n this.render();\n this.startLoop();\n }\n\n destroy(): void {\n this.stop();\n\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n\n if (this.button && this.buttonHandlerAttached) {\n this.button.removeEventListener('click', this.handleButtonClick);\n this.buttonHandlerAttached = false;\n }\n\n this.releaseWebGLResources();\n }\n\n private ensureCanvasCoversContainer(): void {\n if (this.canvas === this.container) return;\n if (!this.container.contains(this.canvas)) return;\n\n if (!this.canvas.style.width) this.canvas.style.width = '100%';\n if (!this.canvas.style.height) this.canvas.style.height = '100%';\n if (!this.canvas.style.display) this.canvas.style.display = 'block';\n }\n\n private syncContainerLayoutVars(): void {\n const target = this.canvas === this.container ? this.canvas : this.container;\n\n target.style.setProperty('--reels', String(this.reels));\n target.style.setProperty('--visible-slots', String(this.visibleSlots));\n target.style.aspectRatio = `${this.reels} / ${this.visibleSlots * this.heightScale}`;\n\n if (this.canvas !== this.container) {\n this.container.style.overflow = 'hidden';\n }\n }\n\n setSegments(initialSegments?: number[]): void {\n this.ensureReady();\n\n const segments = initialSegments ?? [];\n\n for (let r = 0; r < this.reels; r += 1) {\n const segIndex = segments[r] ?? 0;\n const idx = clampInt(segIndex, 0, this.opts.slotCount - 1);\n\n this.offsets[r] = idx * this.frameHeight - this.centerIndex * this.frameHeight;\n this.lastVelOffsets[r] = this.offsets[r];\n this.velocities[r] = 0;\n this.idleBaseOffsets[r] = this.offsets[r];\n }\n\n this.lastVelT = null;\n this.idleStartTime = null;\n this.anim = null;\n\n this.stop();\n this.render();\n this.startLoop();\n }\n\n setSegment(segmentIndex: number): void {\n this.setSegments(new Array(this.reels).fill(segmentIndex));\n }\n\n spinOnce(stopAtSegments: number[], config?: Partial<ReelDealSpinConfig>): Promise<void> {\n this.ensureReady();\n\n const { minSpins, staggerMs, speedPxS, durationMs } = this.getSpinConfig(config);\n const useSpeed = speedPxS > 0;\n const stopSpringOvershootPx = this.getStopSpringOvershootPx();\n const stopSpringSettleMs = Math.max(0, STOP_SPRING_SETTLE_MS);\n const stopSpringEnabled = stopSpringSettleMs > 0 && stopSpringOvershootPx > 0;\n\n const startOffsets = new Array(this.reels);\n const deltas = new Array(this.reels);\n const durations = new Array(this.reels);\n const settleDurationMs = new Array(this.reels);\n const targets = new Array(this.reels);\n\n for (let r = 0; r < this.reels; r += 1) {\n const idx = clampInt(stopAtSegments[r] ?? 0, 0, this.opts.slotCount - 1);\n const desiredTop = idx * this.frameHeight - this.centerIndex * this.frameHeight;\n const desired = mod(desiredTop, this.spriteHeight);\n const current = mod(this.offsets[r], this.spriteHeight);\n\n const alignDelta = mod(current - desired, this.spriteHeight);\n\n let delta = -(minSpins * this.spriteHeight + alignDelta);\n\n if (useSpeed && durationMs > 0) {\n const desiredDistance = (speedPxS * durationMs) / 1000;\n const loops = Math.max(\n minSpins,\n Math.ceil(Math.max(0, desiredDistance - alignDelta) / this.spriteHeight),\n );\n\n delta = -(alignDelta + loops * this.spriteHeight);\n }\n\n if (staggerMs > 0 && useSpeed) {\n const extraDelta = (speedPxS * staggerMs * r) / 1000;\n const extraLoops = Math.ceil(extraDelta / this.spriteHeight);\n\n delta -= extraLoops * this.spriteHeight;\n }\n\n startOffsets[r] = this.offsets[r];\n targets[r] = this.offsets[r] + delta;\n\n let finalDelta = delta;\n let settleDuration = 0;\n\n if (stopSpringEnabled && delta !== 0 && stopSpringOvershootPx > 0) {\n const direction = Math.sign(delta);\n const overshoot = direction * stopSpringOvershootPx;\n\n finalDelta += overshoot;\n settleDuration = stopSpringSettleMs;\n }\n\n deltas[r] = finalDelta;\n settleDurationMs[r] = settleDuration;\n\n if (useSpeed) {\n durations[r] = Math.max(0, Math.round((Math.abs(finalDelta) / speedPxS) * 1000));\n } else {\n durations[r] = Math.max(0, durationMs + staggerMs * r);\n }\n }\n\n const totalDelta = deltas.reduce((acc, v) => acc + Math.abs(v), 0);\n const allSpinZero = durations.every((entry) => entry <= 0);\n const allSettleZero = settleDurationMs.every((entry) => entry <= 0);\n\n if (totalDelta === 0 || (allSpinZero && allSettleZero)) {\n for (let r = 0; r < this.reels; r += 1) {\n this.offsets[r] = targets[r];\n }\n\n this.anim = null;\n this.resolvePendingSpins();\n this.render();\n\n return Promise.resolve();\n }\n\n const startTime = performance.now();\n\n this.anim = {\n startTime,\n startOffsets,\n deltas,\n durationMs: durations,\n settleDurationMs,\n targets,\n };\n this.idleStartTime = null;\n this.startLoop();\n\n return new Promise((resolve) => {\n this.pendingSpinResolvers.push(resolve);\n });\n }\n\n private getSpinConfig(overrides?: Partial<ReelDealSpinConfig>): NormalizedSpinConfig {\n if (!overrides) return this.spinConfig;\n\n return normalizeSpinConfig({ ...this.spinConfig, ...overrides });\n }\n\n private getStopSpringOvershootPx(): number {\n const cap = this.frameHeight * STOP_SPRING_MAX_RATIO;\n const overshoot = Math.max(0, STOP_SPRING_OVERSHOOT_PX);\n\n return Math.min(overshoot, cap);\n }\n\n private getStopSpringOffset(overshoot: number, t: number): number {\n if (overshoot === 0) return 0;\n\n const decay = Math.exp(-STOP_SPRING_DAMPING * t);\n const angle = Math.PI * 2 * STOP_SPRING_OSCILLATIONS * t;\n\n return overshoot * decay * Math.cos(angle);\n }\n\n async spinQueue(spins: ReelDealSpinState[]): Promise<void> {\n for (let i = 0; i < spins.length; i += 1) {\n const spin = spins[i];\n\n await this.spinOnce(spin.stopAtSegments);\n\n spin.callback?.(i, spin.stopAtSegments);\n }\n }\n\n stop(): void {\n if (this.rafId !== null) cancelAnimationFrame(this.rafId);\n\n this.rafId = null;\n this.anim = null;\n this.resolvePendingSpins();\n this.lastVelT = null;\n\n for (let r = 0; r < this.reels; r += 1) {\n this.velocities[r] = 0;\n }\n }\n\n private resolvePendingSpins(): void {\n if (this.pendingSpinResolvers.length === 0) return;\n\n const resolvers = this.pendingSpinResolvers;\n\n this.pendingSpinResolvers = [];\n\n for (const resolve of resolvers) {\n resolve();\n }\n }\n\n requestResize(): void {\n this.queueResize();\n }\n\n private ensureReady(): void {\n if (\n !this.spriteImg ||\n this.spriteWidth <= 0 ||\n this.spriteHeight <= 0 ||\n this.frameHeight <= 0\n ) {\n throw new Error('Sprite is not ready. Call init() and await it first.');\n }\n }\n\n private attachButtonHandler(): void {\n if (!this.button || this.buttonHandlerAttached) return;\n\n this.button.addEventListener('click', this.handleButtonClick, { passive: true });\n this.buttonHandlerAttached = true;\n }\n\n private handleButtonClick = async (): Promise<void> => {\n if (this.spinning) return;\n\n const spin = this.consumeNextSpin() ?? {\n stopAtSegments: this.buildRandomSegments(),\n };\n\n this.spinning = true;\n this.setButtonDisabled(true);\n\n try {\n await this.spinOnce(spin.stopAtSegments);\n } finally {\n if (this.queuedSpins && this.queuedSpins.length === 0) {\n this.setButtonDisabled(true);\n } else {\n this.setButtonDisabled(false);\n }\n\n this.spinning = false;\n }\n };\n\n private resolveButton(): HTMLButtonElement | null {\n if (!this.opts.button) return null;\n\n return resolveElement<HTMLButtonElement>(this.opts.button, 'Button');\n }\n\n private resolveSpinQueue(): ReelDealSpinState[] | null {\n return this.opts.queuedSpinStates ?? null;\n }\n\n private consumeNextSpin(): ReelDealSpinState | null {\n if (!this.queuedSpins) {\n this.queuedSpins = this.resolveSpinQueue();\n }\n\n if (!this.queuedSpins || this.queuedSpins.length === 0) return null;\n\n return this.queuedSpins.shift() ?? null;\n }\n\n private buildRandomSegments(): number[] {\n const max = Math.max(1, Math.floor(this.opts.slotCount));\n const segments: number[] = [];\n\n for (let r = 0; r < this.reels; r += 1) {\n segments.push(Math.floor(Math.random() * max));\n }\n\n return segments;\n }\n\n private setButtonDisabled(disabled: boolean): void {\n if (!this.button) return;\n\n this.button.disabled = disabled;\n\n if (disabled) this.button.setAttribute('aria-busy', 'true');\n else this.button.removeAttribute('aria-busy');\n }\n\n private shouldAnimate(): boolean {\n return this.anim !== null || this.idleBob !== null;\n }\n\n private startLoop(): void {\n if (this.rafId !== null) return;\n if (!this.shouldAnimate()) return;\n\n this.rafId = requestAnimationFrame(this.loop);\n }\n\n private loop = (now: number): void => {\n if (this.anim) {\n const { startTime, durationMs, startOffsets, deltas, settleDurationMs, targets } = this.anim;\n const elapsed = now - startTime;\n\n let allDone = true;\n\n for (let r = 0; r < this.reels; r += 1) {\n const duration = durationMs[r] ?? 0;\n const settleDuration = settleDurationMs[r] ?? 0;\n const targetOffset = targets[r] ?? startOffsets[r] + deltas[r];\n\n if (duration > 0 && elapsed < duration) {\n const t = Math.min(1, elapsed / duration);\n const eased = reelProgress(t);\n\n if (t < 1) allDone = false;\n\n const baseOffset = startOffsets[r] + deltas[r] * eased;\n\n let tick = 0;\n\n if (t > SPIN_TICK_START && this.frameHeight > 0) {\n const u = clamp01((t - SPIN_TICK_START) / SPIN_TICK_RANGE);\n const env = (1 - u) ** 2;\n const phase = (baseOffset / this.frameHeight) * Math.PI * 2;\n const amp = Math.min(SPIN_TICK_MAX_PX, this.frameHeight * 0.06);\n\n tick = Math.sin(phase) * env * amp;\n }\n\n this.offsets[r] = baseOffset + tick;\n } else if (settleDuration > 0) {\n const t =\n duration <= 0 ? elapsed / settleDuration : (elapsed - duration) / settleDuration;\n const u = clamp01(t);\n const overshoot = startOffsets[r] + deltas[r] - targetOffset;\n const spring = this.getStopSpringOffset(overshoot, u);\n\n if (u < 1) allDone = false;\n\n this.offsets[r] = targetOffset + spring;\n } else {\n this.offsets[r] = targetOffset;\n }\n }\n\n this.updateVelocity(now);\n this.render();\n\n if (allDone) {\n for (let r = 0; r < this.reels; r += 1) {\n const targetOffset =\n this.anim.targets[r] ?? this.anim.startOffsets[r] + this.anim.deltas[r];\n\n this.offsets[r] = targetOffset;\n this.idleBaseOffsets[r] = this.offsets[r];\n }\n\n this.updateVelocity(now);\n\n for (let r = 0; r < this.reels; r += 1) {\n this.velocities[r] = 0;\n }\n\n this.anim = null;\n this.idleStartTime = null;\n\n this.render();\n this.resolvePendingSpins();\n }\n } else if (this.idleBob) {\n this.updateIdle(now);\n this.render();\n }\n\n if (!this.shouldAnimate()) {\n this.rafId = null;\n\n return;\n }\n\n if (this.rafId === null) return;\n\n this.rafId = requestAnimationFrame(this.loop);\n };\n\n private render(): void {\n if (!this.spriteImg) return;\n if (this.width <= 0 || this.height <= 0) return;\n if (!this.gl) return;\n\n this.renderWebGL();\n }\n\n private getSpinBlurPxForReel(reel: number): number {\n if (!this.anim) return 0;\n\n const speed = Math.abs(this.velocities[reel] ?? 0);\n const speedAtMax = Math.max(1, this.spinBlur.speedAtMax);\n const t = clamp01(speed / speedAtMax);\n\n return t * Math.max(0, this.spinBlur.maxPx);\n }\n\n private renderWebGL(): void {\n if (\n !this.gl ||\n !this.glProgram ||\n !this.glAttribs ||\n !this.glUniforms ||\n !this.glTexture ||\n !this.glBuffer\n ) {\n return;\n }\n\n if (!this.spriteImg) return;\n\n const gl = this.gl;\n\n gl.useProgram(this.glProgram);\n gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffer);\n\n gl.enableVertexAttribArray(this.glAttribs.pos);\n gl.vertexAttribPointer(this.glAttribs.pos, 2, gl.FLOAT, false, 16, 0);\n gl.enableVertexAttribArray(this.glAttribs.uv);\n gl.vertexAttribPointer(this.glAttribs.uv, 2, gl.FLOAT, false, 16, 8);\n\n gl.activeTexture(gl.TEXTURE0);\n gl.bindTexture(gl.TEXTURE_2D, this.glTexture);\n gl.uniform1i(this.glUniforms.texture, 0);\n\n gl.uniform1f(this.glUniforms.frameHeight, this.frameHeight);\n gl.uniform1f(this.glUniforms.spriteHeight, this.spriteHeight);\n gl.uniform1f(this.glUniforms.visibleSlots, this.visibleSlots);\n gl.uniform1f(this.glUniforms.heightScale, this.heightScale);\n gl.uniform1f(this.glUniforms.edgePower, Math.max(0.5, this.webglShading.edgePower));\n\n const shading = this.webglShading;\n const warpAngle = (Math.max(0, shading.warpAngleDeg) * Math.PI) / 180;\n\n gl.uniform1f(this.glUniforms.warpAngle, warpAngle);\n gl.clear(gl.COLOR_BUFFER_BIT);\n\n const reelW = 1 / this.reels;\n\n for (let r = 0; r < this.reels; r += 1) {\n gl.uniform1f(this.glUniforms.reelX, r * reelW);\n gl.uniform1f(this.glUniforms.reelW, reelW);\n\n const offsetPx = mod(this.offsets[r], this.spriteHeight);\n\n gl.uniform1f(this.glUniforms.offsetPx, offsetPx);\n gl.uniform1f(this.glUniforms.blurPx, this.getSpinBlurPxForReel(r));\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n }\n }\n\n private updateVelocity(now: number): void {\n if (this.lastVelT === null) {\n this.lastVelT = now;\n\n for (let r = 0; r < this.reels; r += 1) {\n this.lastVelOffsets[r] = this.offsets[r];\n this.velocities[r] = 0;\n }\n\n return;\n }\n\n const dt = (now - this.lastVelT) / 1000;\n\n if (dt <= 0) return;\n\n for (let r = 0; r < this.reels; r += 1) {\n const v = (this.offsets[r] - this.lastVelOffsets[r]) / dt;\n\n this.velocities[r] = this.velocities[r] * VELOCITY_DECAY + v * VELOCITY_GAIN;\n this.lastVelOffsets[r] = this.offsets[r];\n }\n\n this.lastVelT = now;\n }\n\n private updateIdle(now: number): void {\n if (!this.idleBob) return;\n if (this.idleStartTime === null) this.idleStartTime = now;\n\n const { amplitudePx, speedHz, phaseOffsetRad, rampMs } = this.idleBob;\n const elapsedMs = now - this.idleStartTime;\n\n const tSec = elapsedMs / 1000;\n const rampT = Math.max(0, Math.min(1, elapsedMs / Math.max(1, rampMs)));\n const ramp = rampHermite(rampT);\n const omega = speedHz * Math.PI * 2;\n\n for (let r = 0; r < this.reels; r += 1) {\n const phase = tSec * omega + r * phaseOffsetRad;\n\n this.offsets[r] = this.idleBaseOffsets[r] + Math.sin(phase) * (amplitudePx * ramp);\n this.velocities[r] = 0;\n }\n }\n\n private async loadSprite(): Promise<void> {\n const { sprite } = this.opts;\n const img = typeof sprite === 'string' ? new Image() : sprite;\n\n if (typeof sprite === 'string') {\n img.src = sprite;\n }\n\n if (!img.complete || img.naturalWidth <= 0) {\n await new Promise<void>((resolve, reject) => {\n const onLoad = () => resolve();\n const onError = () => reject(new Error('Failed to load sprite image'));\n\n img.addEventListener('load', onLoad, { once: true });\n img.addEventListener('error', onError, { once: true });\n });\n }\n\n const slotCount = Math.max(1, Math.floor(this.opts.slotCount));\n const spriteWidth = img.naturalWidth;\n const frameHeight = spriteWidth;\n const expectedSpriteHeight = frameHeight * slotCount;\n\n const naturalHeight = img.naturalHeight;\n const diff = Math.abs(naturalHeight - expectedSpriteHeight);\n\n if (diff > 2) {\n throw new Error(\n `Sprite size mismatch. Expected height ~${Math.round(expectedSpriteHeight)}px (slotCount=${slotCount}, slot aspect 1/1), got ${naturalHeight}px.`,\n );\n }\n\n this.spriteImg = img;\n this.spriteWidth = spriteWidth;\n this.frameHeight = Math.max(1, Math.round(frameHeight));\n this.spriteHeight = Math.max(1, Math.round(this.frameHeight * slotCount));\n }\n\n private setupResizeWatcher(): void {\n if (typeof ResizeObserver === 'undefined') return;\n if (this.resizeObserver) return;\n\n this.resizeObserver = new ResizeObserver(() => this.queueResize());\n this.resizeObserver.observe(this.container);\n }\n\n private queueResize = (): void => {\n if (this.resizeQueued) return;\n\n this.resizeQueued = true;\n\n requestAnimationFrame(() => {\n this.resizeQueued = false;\n this.resizeToContainer();\n });\n };\n\n private resizeToContainer(): void {\n const cssWidth = this.container.clientWidth;\n const cssHeight = this.container.clientHeight;\n const slotsViewport = this.visibleSlots * this.heightScale;\n\n if (cssWidth <= 0 || cssHeight <= 0) {\n this.applyResize(1, 1);\n\n return;\n }\n\n const slots = slotsViewport;\n const reels = this.reels;\n const fitByWidthH = (cssWidth / reels) * slots;\n\n let w = cssWidth;\n let h = fitByWidthH;\n\n if (h > cssHeight) {\n h = cssHeight;\n w = (cssHeight / slots) * reels;\n }\n\n const viewportWidth = Math.max(1, Math.floor(w));\n const viewportHeight = Math.max(1, Math.floor(h));\n\n this.applyResize(viewportWidth, viewportHeight);\n this.render();\n }\n\n private applyViewportCrop(): void {\n this.canvas.style.width = '100%';\n this.canvas.style.height = '100%';\n this.canvas.style.transform = '';\n }\n\n private applyResize(width: number, height: number): void {\n const normalizedWidth = Math.max(1, Math.floor(width));\n const normalizedHeight = Math.max(1, Math.floor(height));\n\n const dpr = this.getTargetDpr();\n\n this.width = normalizedWidth;\n this.height = normalizedHeight;\n this.dpr = dpr;\n\n this.canvas.width = Math.floor(this.width * this.dpr);\n this.canvas.height = Math.floor(this.height * this.dpr);\n\n if (this.gl) {\n this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);\n }\n\n this.applyViewportCrop();\n }\n\n private getTargetDpr(): number {\n const rawDpr = window.devicePixelRatio || 1;\n const base = Math.max(1, rawDpr);\n\n return Math.min(this.maxDpr, base);\n }\n\n private tryInitWebGL(): void {\n if (!this.spriteImg) return;\n\n this.releaseWebGLResources();\n this.webglInitError = null;\n\n const gl =\n (this.canvas.getContext('webgl2', {\n antialias: true,\n alpha: true,\n }) as WebGL2RenderingContext | null) ??\n (this.canvas.getContext('webgl', {\n antialias: true,\n alpha: true,\n }) as WebGLRenderingContext | null) ??\n (this.canvas.getContext('experimental-webgl', {\n antialias: true,\n alpha: true,\n }) as WebGLRenderingContext | null);\n\n if (!gl) {\n const fallback = document.createElement('canvas');\n const canWebGL = typeof WebGLRenderingContext !== 'undefined';\n const fallbackGl =\n (fallback.getContext('webgl2') as WebGL2RenderingContext | null) ??\n (fallback.getContext('webgl') as WebGLRenderingContext | null);\n\n this.webglInitError = `context is null (browser WebGL=${canWebGL ? 'yes' : 'no'}, fallback canvas=${fallbackGl ? 'yes' : 'no'})`;\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n return;\n }\n\n const vsSource = `\n attribute vec2 a_pos;\n attribute vec2 a_uv;\n varying vec2 v_uv;\n void main() {\n v_uv = a_uv;\n gl_Position = vec4(a_pos, 0.0, 1.0);\n }\n `;\n\n const fsSource = `\n #ifdef GL_FRAGMENT_PRECISION_HIGH\n precision highp float;\n #else\n precision mediump float;\n #endif\n\n varying vec2 v_uv;\n uniform sampler2D u_tex;\n uniform float u_reelX;\n uniform float u_reelW;\n uniform float u_offsetPx;\n uniform float u_frameHeight;\n uniform float u_spriteHeight;\n uniform float u_visibleSlots;\n uniform float u_heightScale;\n uniform float u_edgePower;\n uniform float u_warpAngle;\n uniform float u_blurPx;\n\n const float HALF_PI = 1.5707963;\n\n float saturate(float x) { return clamp(x, 0.0, 1.0); }\n\n void main() {\n if (v_uv.x < u_reelX || v_uv.x > (u_reelX + u_reelW)) {\n discard;\n }\n\n float localX = (v_uv.x - u_reelX) / u_reelW;\n float localY = v_uv.y;\n float hs = max(0.0001, u_heightScale);\n float fullY = localY * hs + (1.0 - hs) * 0.5;\n\n float warp = max(0.0, u_warpAngle);\n float warpStrength = saturate(warp / 1.2);\n float center = 0.5;\n float centerBand = 1.0 / 3.0;\n float halfBand = centerBand * 0.5;\n float dist = abs(fullY - center);\n float edgeWeight = smoothstep(halfBand, 0.5, dist);\n float edgePow = max(0.5, u_edgePower);\n edgeWeight = pow(edgeWeight, edgePow) * warpStrength;\n float yCentered = (fullY - 0.5) * 2.0;\n float sinY = 0.5 + 0.5 * sin(yCentered * HALF_PI);\n float warpedY = mix(fullY, sinY, edgeWeight);\n\n float yPx = u_offsetPx + warpedY * (u_frameHeight * u_visibleSlots);\n float v = fract((u_offsetPx / u_spriteHeight) +\n warpedY * ((u_frameHeight * u_visibleSlots) / u_spriteHeight));\n\n vec4 base = texture2D(u_tex, vec2(localX, v));\n\n if (u_blurPx > 0.0) {\n float maxStepPx = 1.0 + 3.0 * saturate(u_blurPx / 4.0);\n float blurPx = min(u_blurPx, maxStepPx);\n float blurStep = blurPx / max(1.0, u_spriteHeight);\n vec4 sum = base * 0.227027;\n sum += texture2D(u_tex, vec2(localX, fract(v + blurStep))) * 0.1945946;\n sum += texture2D(u_tex, vec2(localX, fract(v - blurStep))) * 0.1945946;\n sum += texture2D(u_tex, vec2(localX, fract(v + 2.0 * blurStep))) * 0.1216216;\n sum += texture2D(u_tex, vec2(localX, fract(v - 2.0 * blurStep))) * 0.1216216;\n sum += texture2D(u_tex, vec2(localX, fract(v + 3.0 * blurStep))) * 0.054054;\n sum += texture2D(u_tex, vec2(localX, fract(v - 3.0 * blurStep))) * 0.054054;\n sum += texture2D(u_tex, vec2(localX, fract(v + 4.0 * blurStep))) * 0.016216;\n sum += texture2D(u_tex, vec2(localX, fract(v - 4.0 * blurStep))) * 0.016216;\n base = sum;\n }\n\n gl_FragColor = base;\n }\n `;\n\n const program = this.createWebGLProgram(gl, vsSource, fsSource);\n\n if (!program) {\n this.webglInitError = 'shader program failed to compile/link (see console)';\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n return;\n }\n\n const buffer = gl.createBuffer();\n\n if (!buffer) {\n this.webglInitError = 'failed to create vertex buffer';\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n gl.deleteProgram(program);\n\n return;\n }\n\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n\n const quad = new Float32Array([-1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0, 1, 1, 1, 0]);\n\n gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n\n const texture = gl.createTexture();\n\n if (!texture) {\n this.webglInitError = 'failed to create texture';\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n gl.deleteBuffer(buffer);\n gl.deleteProgram(program);\n\n return;\n }\n\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.spriteImg);\n\n const isPowerOfTwo = (n: number): boolean => n > 0 && (n & (n - 1)) === 0;\n const pot = isPowerOfTwo(this.spriteWidth) && isPowerOfTwo(this.spriteHeight);\n\n if (pot) {\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);\n gl.generateMipmap(gl.TEXTURE_2D);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n } else {\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n }\n\n gl.useProgram(program);\n gl.clearColor(0, 0, 0, 0);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\n\n this.gl = gl;\n this.glProgram = program;\n this.glBuffer = buffer;\n this.glTexture = texture;\n this.glAttribs = {\n pos: gl.getAttribLocation(program, 'a_pos'),\n uv: gl.getAttribLocation(program, 'a_uv'),\n };\n this.glUniforms = {\n texture: gl.getUniformLocation(program, 'u_tex'),\n reelX: gl.getUniformLocation(program, 'u_reelX'),\n reelW: gl.getUniformLocation(program, 'u_reelW'),\n offsetPx: gl.getUniformLocation(program, 'u_offsetPx'),\n frameHeight: gl.getUniformLocation(program, 'u_frameHeight'),\n spriteHeight: gl.getUniformLocation(program, 'u_spriteHeight'),\n visibleSlots: gl.getUniformLocation(program, 'u_visibleSlots'),\n heightScale: gl.getUniformLocation(program, 'u_heightScale'),\n edgePower: gl.getUniformLocation(program, 'u_edgePower'),\n warpAngle: gl.getUniformLocation(program, 'u_warpAngle'),\n blurPx: gl.getUniformLocation(program, 'u_blurPx'),\n };\n }\n\n private createWebGLProgram(\n gl: WebGLRenderingContext | WebGL2RenderingContext,\n vsSource: string,\n fsSource: string,\n ): WebGLProgram | null {\n const vs = this.createWebGLShader(gl, gl.VERTEX_SHADER, vsSource);\n const fs = this.createWebGLShader(gl, gl.FRAGMENT_SHADER, fsSource);\n\n if (!vs || !fs) {\n if (vs) gl.deleteShader(vs);\n if (fs) gl.deleteShader(fs);\n\n return null;\n }\n\n const program = gl.createProgram();\n\n if (!program) return null;\n\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const info = gl.getProgramInfoLog(program) ?? 'unknown link error';\n\n this.webglInitError = `program link failed: ${info}`;\n\n console.warn('ReelDeal WebGL: program link failed', info);\n\n gl.deleteProgram(program);\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n\n return null;\n }\n\n gl.detachShader(program, vs);\n gl.detachShader(program, fs);\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n\n return program;\n }\n\n private createWebGLShader(\n gl: WebGLRenderingContext | WebGL2RenderingContext,\n type: number,\n source: string,\n ): WebGLShader | null {\n const shader = gl.createShader(type);\n\n if (!shader) return null;\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n const info = gl.getShaderInfoLog(shader) ?? 'unknown compile error';\n\n this.webglInitError = `shader compile failed: ${info}`;\n\n console.warn('ReelDeal WebGL: shader compile failed', info);\n\n gl.deleteShader(shader);\n\n return null;\n }\n\n return shader;\n }\n\n private releaseWebGLResources(): void {\n if (!this.gl) return;\n if (this.glTexture) this.gl.deleteTexture(this.glTexture);\n if (this.glBuffer) this.gl.deleteBuffer(this.glBuffer);\n if (this.glProgram) this.gl.deleteProgram(this.glProgram);\n\n this.glTexture = null;\n this.glBuffer = null;\n this.glProgram = null;\n this.glAttribs = null;\n this.glUniforms = null;\n this.gl = null;\n }\n}\n"],"names":["defaultSpinConfig","defaultSpinBlur","defaultIdleBob","SPIN_ACCEL_END","SPIN_DECEL_START","clampInt","value","min","max","mod","n","m","clamp01","t","rampHermite","u","x","brakeHermite","reelProgress","tt","finiteOr","fallback","nonNegative","nonNegativeInt","DEFAULT_MAX_DPR","normalizeSpinConfig","overrides","base","normalizeSpinBlur","normalizeIdleBob","normalizeMaxDpr","resolveElementById","id","kind","el","resolveElement","elOrId","MAX_REELS","SPIN_TICK_START","SPIN_TICK_RANGE","SPIN_TICK_MAX_PX","VELOCITY_DECAY","VELOCITY_GAIN","STOP_SPRING_OVERSHOOT_PX","STOP_SPRING_SETTLE_MS","STOP_SPRING_OSCILLATIONS","STOP_SPRING_MAX_RATIO","DEFAULT_WEBGL_SHADING","normalizeWebGLShading","ReelDeal","options","target","initialSegments","segments","r","segIndex","idx","segmentIndex","stopAtSegments","config","minSpins","staggerMs","speedPxS","durationMs","useSpeed","stopSpringOvershootPx","stopSpringSettleMs","stopSpringEnabled","startOffsets","deltas","durations","settleDurationMs","targets","desiredTop","desired","current","alignDelta","delta","desiredDistance","loops","extraDelta","extraLoops","finalDelta","settleDuration","overshoot","totalDelta","acc","v","allSpinZero","entry","allSettleZero","startTime","resolve","cap","decay","angle","spins","i","spin","resolvers","disabled","now","elapsed","allDone","duration","targetOffset","eased","baseOffset","tick","env","phase","amp","spring","reel","speed","speedAtMax","gl","shading","warpAngle","reelW","offsetPx","dt","amplitudePx","speedHz","phaseOffsetRad","rampMs","elapsedMs","tSec","rampT","ramp","omega","sprite","img","reject","onLoad","onError","slotCount","spriteWidth","frameHeight","expectedSpriteHeight","naturalHeight","cssWidth","cssHeight","slotsViewport","slots","reels","fitByWidthH","w","h","viewportWidth","viewportHeight","width","height","normalizedWidth","normalizedHeight","dpr","rawDpr","canWebGL","fallbackGl","program","buffer","quad","texture","isPowerOfTwo","vsSource","fsSource","vs","fs","info","type","source","shader"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/assets/spriteLoader.ts","../src/defaults.ts","../src/utils/math.ts","../src/normalize.ts","../src/core/animation.ts","../src/core/loop.ts","../src/core/spinQueue.ts","../src/core/uiControls.ts","../src/render/shaders.ts","../src/render/lineRenderer.ts","../src/render/webglRenderer.ts","../src/utils/dom.ts","../src/ReelDeal.ts"],"sourcesContent":["import type { ReelDealSpriteSource } from '../types';\n\nexport type LoadedSprite = {\n image: HTMLImageElement;\n spriteWidth: number;\n spriteHeight: number;\n frameHeight: number;\n slotCount: number;\n};\n\nconst waitImageLoad = (img: HTMLImageElement): Promise<void> =>\n new Promise<void>((resolve, reject) => {\n const onLoad = (): void => resolve();\n const onError = (): void => reject(new Error('Failed to load sprite image'));\n\n img.addEventListener('load', onLoad, { once: true });\n img.addEventListener('error', onError, { once: true });\n });\n\nexport const loadSprite = async (\n sprite: ReelDealSpriteSource,\n slotCountRaw: number,\n): Promise<LoadedSprite> => {\n const img = typeof sprite === 'string' ? new Image() : sprite;\n\n if (typeof sprite === 'string') {\n img.src = sprite;\n }\n\n if (!img.complete || img.naturalWidth <= 0) {\n await waitImageLoad(img);\n }\n\n const slotCount = Math.max(1, Math.floor(slotCountRaw));\n const spriteWidth = img.naturalWidth;\n const frameHeight = spriteWidth;\n const expectedSpriteHeight = frameHeight * slotCount;\n\n const naturalHeight = img.naturalHeight;\n const diff = Math.abs(naturalHeight - expectedSpriteHeight);\n\n if (diff > 2) {\n throw new Error(\n `Sprite size mismatch. Expected height ~${Math.round(expectedSpriteHeight)}px (slotCount=${slotCount}, slot aspect 1/1), got ${naturalHeight}px.`,\n );\n }\n\n return {\n image: img,\n spriteWidth,\n frameHeight: Math.max(1, Math.round(frameHeight)),\n spriteHeight: Math.max(1, Math.round(frameHeight * slotCount)),\n slotCount,\n };\n};\n","import type { ReelDealIdleBob, ReelDealSpinBlur, ReelDealSpinConfig } from './types';\n\nexport const defaultSpinConfig: ReelDealSpinConfig = {\n minSpins: 2,\n durationMs: 4000,\n speedPxS: 4000,\n staggerMs: 300,\n};\n\nexport const defaultSpinBlur: ReelDealSpinBlur = {\n maxPx: 4,\n speedAtMax: 2200,\n};\n\nexport const defaultIdleBob: ReelDealIdleBob = {\n amplitudePx: 9,\n speedHz: 0.25,\n phaseOffsetRad: 0.9,\n rampMs: 800,\n};\n\nexport const defaultWebglShading = {\n warpAngleDeg: 60,\n edgePower: 4.8,\n} as const;\n\nexport const defaultFinalSpinLine = {\n thickness: 6,\n intensity: 1.0,\n} as const;\n","const SPIN_ACCEL_END = 0.1;\nconst SPIN_DECEL_START = 0.6;\n\nexport const clampInt = (value: number, min: number, max: number): number =>\n Math.max(min, Math.min(max, Math.floor(value)));\n\nexport const mod = (n: number, m: number): number => ((n % m) + m) % m;\n\nexport const clamp01 = (t: number): number => Math.max(0, Math.min(1, t));\n\nexport const rampHermite = (u: number): number => {\n const x = clamp01(u);\n\n return x ** 2 * (2 - x);\n};\n\nexport const brakeHermite = (u: number): number => {\n const x = clamp01(u);\n\n return x + x ** 2 - x ** 3;\n};\n\nexport const reelProgress = (t: number): number => {\n const tt = clamp01(t);\n\n if (tt <= SPIN_ACCEL_END) {\n const u = tt / SPIN_ACCEL_END;\n\n return rampHermite(u) * SPIN_ACCEL_END;\n }\n\n if (tt < SPIN_DECEL_START) {\n return tt;\n }\n\n const u = (tt - SPIN_DECEL_START) / (1 - SPIN_DECEL_START);\n\n return SPIN_DECEL_START + brakeHermite(u) * (1 - SPIN_DECEL_START);\n};\n\nexport const finiteOr = (value: number | undefined, fallback: number): number =>\n typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n\nexport const nonNegative = (value: number | undefined, fallback: number): number =>\n Math.max(0, finiteOr(value, fallback));\n\nexport const nonNegativeInt = (value: number | undefined, fallback: number): number =>\n Math.max(0, Math.floor(finiteOr(value, fallback)));\n","import {\n defaultFinalSpinLine,\n defaultIdleBob,\n defaultSpinBlur,\n defaultSpinConfig,\n defaultWebglShading,\n} from './defaults';\nimport type {\n ReelDealFinalSpinLine,\n ReelDealIdleBob,\n ReelDealSpinBlur,\n ReelDealSpinConfig,\n ReelDealWebGLShading,\n} from './types';\nimport { clamp01, finiteOr, nonNegative, nonNegativeInt } from './utils/math';\n\nconst DEFAULT_MAX_DPR = 2;\n\ntype NormalizedSpinConfig = Required<ReelDealSpinConfig>;\n\nexport const normalizeSpinConfig = (\n overrides?: Partial<ReelDealSpinConfig>,\n): NormalizedSpinConfig => {\n const base = { ...defaultSpinConfig, ...(overrides ?? {}) };\n\n return {\n minSpins: nonNegativeInt(base.minSpins, defaultSpinConfig.minSpins ?? 0),\n durationMs: nonNegativeInt(base.durationMs, defaultSpinConfig.durationMs ?? 0),\n speedPxS: nonNegativeInt(base.speedPxS, defaultSpinConfig.speedPxS ?? 0),\n staggerMs: nonNegativeInt(base.staggerMs, defaultSpinConfig.staggerMs ?? 0),\n };\n};\n\nexport const normalizeSpinBlur = (overrides?: Partial<ReelDealSpinBlur>): ReelDealSpinBlur => {\n const base = { ...defaultSpinBlur, ...(overrides ?? {}) };\n\n return {\n maxPx: nonNegative(base.maxPx, defaultSpinBlur.maxPx),\n speedAtMax: nonNegative(base.speedAtMax, defaultSpinBlur.speedAtMax),\n };\n};\n\nexport const normalizeIdleBob = (\n overrides?: Partial<ReelDealIdleBob> | false,\n): ReelDealIdleBob | null => {\n if (overrides === false) return null;\n\n const base = { ...defaultIdleBob, ...(overrides ?? {}) };\n\n return {\n amplitudePx: nonNegative(base.amplitudePx, defaultIdleBob.amplitudePx),\n speedHz: nonNegative(base.speedHz, defaultIdleBob.speedHz),\n phaseOffsetRad: finiteOr(base.phaseOffsetRad, defaultIdleBob.phaseOffsetRad),\n rampMs: nonNegativeInt(base.rampMs, defaultIdleBob.rampMs),\n };\n};\n\nexport const normalizeMaxDpr = (value?: number): number =>\n Math.max(1, finiteOr(value, DEFAULT_MAX_DPR));\n\nexport const normalizeWebGLShading = (\n overrides?: Partial<ReelDealWebGLShading>,\n): ReelDealWebGLShading => ({\n warpAngleDeg: nonNegative(overrides?.warpAngleDeg, defaultWebglShading.warpAngleDeg),\n edgePower: nonNegative(overrides?.edgePower, defaultWebglShading.edgePower),\n});\n\nexport const normalizeFinalSpinLine = (\n overrides?: Partial<ReelDealFinalSpinLine> | false,\n): ReelDealFinalSpinLine | null => {\n if (overrides === false) return null;\n if (!overrides) return null;\n\n return {\n thickness: nonNegative(overrides.thickness, defaultFinalSpinLine.thickness),\n intensity: clamp01(overrides.intensity ?? defaultFinalSpinLine.intensity),\n };\n};\n","import type { ReelDealIdleBob, ReelDealSpinConfig } from '../types';\nimport { clampInt, mod } from '../utils/math';\n\ntype NumericArray = number[] | Float32Array | Int32Array | Uint32Array;\n\nexport type SpinPlan = {\n startOffsets: number[];\n deltas: number[];\n durations: number[];\n settleDurationMs: number[];\n targets: number[];\n totalDelta: number;\n allSpinZero: boolean;\n allSettleZero: boolean;\n};\n\nexport type SpinPlanInput = {\n stopAtSegments: NumericArray;\n currentOffsets: NumericArray;\n reels: number;\n frameHeight: number;\n spriteHeight: number;\n centerIndex: number;\n slotCount: number;\n spinConfig: Required<ReelDealSpinConfig>;\n stopSpringOvershootPx: number;\n stopSpringSettleMs: number;\n useSpeed: boolean;\n};\n\nexport const buildSpinPlan = (input: SpinPlanInput): SpinPlan => {\n const {\n stopAtSegments,\n currentOffsets,\n reels,\n frameHeight,\n spriteHeight,\n centerIndex,\n slotCount,\n spinConfig,\n stopSpringOvershootPx,\n stopSpringSettleMs,\n useSpeed,\n } = input;\n\n const startOffsets = new Array(reels);\n const deltas = new Array(reels);\n const durations = new Array(reels);\n const settleDurationMs = new Array(reels);\n const targets = new Array(reels);\n\n const { minSpins, staggerMs, speedPxS, durationMs } = spinConfig;\n const stopSpringEnabled = stopSpringSettleMs > 0 && stopSpringOvershootPx > 0;\n\n for (let r = 0; r < reels; r += 1) {\n const idx = clampInt(stopAtSegments[r] ?? 0, 0, slotCount - 1);\n const desiredTop = idx * frameHeight - centerIndex * frameHeight;\n const desired = mod(desiredTop, spriteHeight);\n const current = mod(currentOffsets[r], spriteHeight);\n\n const alignDelta = mod(current - desired, spriteHeight);\n\n let delta = -(minSpins * spriteHeight + alignDelta);\n\n if (useSpeed && durationMs > 0) {\n const desiredDistance = (speedPxS * durationMs) / 1000;\n const loops = Math.max(\n minSpins,\n Math.ceil(Math.max(0, desiredDistance - alignDelta) / spriteHeight),\n );\n\n delta = -(alignDelta + loops * spriteHeight);\n }\n\n if (staggerMs > 0 && useSpeed) {\n const extraDelta = (speedPxS * staggerMs * r) / 1000;\n const extraLoops = Math.ceil(extraDelta / spriteHeight);\n\n delta -= extraLoops * spriteHeight;\n }\n\n startOffsets[r] = currentOffsets[r];\n targets[r] = currentOffsets[r] + delta;\n\n let finalDelta = delta;\n let settleDuration = 0;\n\n if (stopSpringEnabled && delta !== 0 && stopSpringOvershootPx > 0) {\n const direction = Math.sign(delta);\n const overshoot = direction * stopSpringOvershootPx;\n\n finalDelta += overshoot;\n settleDuration = stopSpringSettleMs;\n }\n\n deltas[r] = finalDelta;\n settleDurationMs[r] = settleDuration;\n\n if (useSpeed) {\n durations[r] = Math.max(0, Math.round((Math.abs(finalDelta) / speedPxS) * 1000));\n } else {\n durations[r] = Math.max(0, durationMs + staggerMs * r);\n }\n }\n\n const totalDelta = deltas.reduce((acc, v) => acc + Math.abs(v), 0);\n const allSpinZero = durations.every((entry) => entry <= 0);\n const allSettleZero = settleDurationMs.every((entry) => entry <= 0);\n\n return {\n startOffsets,\n deltas,\n durations,\n settleDurationMs,\n targets,\n totalDelta,\n allSpinZero,\n allSettleZero,\n };\n};\n\nexport const getStopSpringOffset = (overshoot: number, t: number): number => {\n if (overshoot === 0) return 0;\n\n const decay = Math.exp(-5 * t);\n const angle = Math.PI * 2 * 1.25 * t;\n\n return overshoot * decay * Math.cos(angle);\n};\n\nexport type VelocityState = {\n lastVelT: number | null;\n lastVelOffsets: NumericArray;\n velocities: NumericArray;\n};\n\nexport const updateVelocities = (\n now: number,\n state: VelocityState,\n offsets: NumericArray,\n): number | null => {\n if (state.lastVelT === null) {\n state.lastVelT = now;\n\n for (let r = 0; r < offsets.length; r += 1) {\n state.lastVelOffsets[r] = offsets[r];\n state.velocities[r] = 0;\n }\n\n return state.lastVelT;\n }\n\n const dt = (now - state.lastVelT) / 1000;\n\n if (dt <= 0) return state.lastVelT;\n\n const VELOCITY_DECAY = 0.85;\n const VELOCITY_GAIN = 1 - VELOCITY_DECAY;\n\n for (let r = 0; r < offsets.length; r += 1) {\n const v = (offsets[r] - state.lastVelOffsets[r]) / dt;\n\n state.velocities[r] = state.velocities[r] * VELOCITY_DECAY + v * VELOCITY_GAIN;\n state.lastVelOffsets[r] = offsets[r];\n }\n\n state.lastVelT = now;\n\n return state.lastVelT;\n};\n\nexport type IdleState = {\n idleStartTime: number | null;\n idleBaseOffsets: NumericArray;\n velocities: NumericArray;\n};\n\nexport const updateIdleOffsets = (\n now: number,\n idleState: IdleState,\n idleBob: ReelDealIdleBob,\n offsets: NumericArray,\n reels: number,\n): number | null => {\n if (idleState.idleStartTime === null) idleState.idleStartTime = now;\n\n const { amplitudePx, speedHz, phaseOffsetRad, rampMs } = idleBob;\n const elapsedMs = now - idleState.idleStartTime;\n\n const tSec = elapsedMs / 1000;\n const rampT = Math.max(0, Math.min(1, elapsedMs / Math.max(1, rampMs)));\n const ramp = Math.min(1, Math.max(0, rampT * rampT * (2 - rampT)));\n const omega = speedHz * Math.PI * 2;\n\n for (let r = 0; r < reels; r += 1) {\n const phase = tSec * omega + r * phaseOffsetRad;\n\n offsets[r] = idleState.idleBaseOffsets[r] + Math.sin(phase) * (amplitudePx * ramp);\n idleState.velocities[r] = 0;\n }\n\n return idleState.idleStartTime;\n};\n","export type RafStep = (now: number) => boolean | undefined;\n\nexport class RafLoop {\n private rafId: number | null = null;\n private step: RafStep | null = null;\n private abortListener: (() => void) | null = null;\n\n start(step: RafStep, signal?: AbortSignal): void {\n if (this.rafId !== null) return;\n\n this.step = step;\n this.attachAbort(signal);\n this.rafId = requestAnimationFrame(this.tick);\n }\n\n stop(): void {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n\n this.step = null;\n this.detachAbort();\n }\n\n isRunning(): boolean {\n return this.rafId !== null;\n }\n\n private tick = (now: number): void => {\n if (!this.step) {\n this.stop();\n\n return;\n }\n\n const shouldContinue = this.step(now);\n\n if (shouldContinue === false) {\n this.stop();\n\n return;\n }\n\n if (this.rafId === null) return;\n\n this.rafId = requestAnimationFrame(this.tick);\n };\n\n private attachAbort(signal?: AbortSignal): void {\n if (!signal) return;\n if (signal.aborted) {\n this.stop();\n return;\n }\n\n const onAbort = (): void => {\n this.stop();\n };\n\n signal.addEventListener('abort', onAbort, { once: true });\n\n this.abortListener = () => signal.removeEventListener('abort', onAbort);\n }\n\n private detachAbort(): void {\n if (this.abortListener) {\n this.abortListener();\n }\n\n this.abortListener = null;\n }\n}\n","import type { ReelDealSpinState } from '../types';\nimport { clampInt } from '../utils/math';\n\nexport type SpinQueueConfig = {\n slotCount: number;\n reels: number;\n initialQueue?: ReelDealSpinState[] | null;\n};\n\nexport type SpinQueueEntry = {\n state: ReelDealSpinState;\n remaining: number;\n isLast: boolean;\n};\n\nexport class SpinQueueController {\n private readonly slotCount: number;\n private readonly reels: number;\n private queue: ReelDealSpinState[];\n\n constructor(config: SpinQueueConfig) {\n this.slotCount = Math.max(1, Math.floor(config.slotCount));\n this.reels = Math.max(1, Math.floor(config.reels));\n this.queue = [...(config.initialQueue ?? [])];\n }\n\n reset(queue: ReelDealSpinState[] | null | undefined): void {\n this.queue = [...(queue ?? [])];\n }\n\n hasPending(): boolean {\n return this.queue.length > 0;\n }\n\n consume(): SpinQueueEntry {\n if (this.queue.length === 0) {\n return {\n state: { stopAtSegments: this.buildRandomSegments() },\n remaining: 0,\n isLast: true,\n };\n }\n\n const state = this.queue.shift() ?? { stopAtSegments: this.buildRandomSegments() };\n\n return {\n state,\n remaining: this.queue.length,\n isLast: this.queue.length === 0,\n };\n }\n\n private buildRandomSegments(): number[] {\n const max = this.slotCount;\n const segments: number[] = [];\n\n for (let r = 0; r < this.reels; r += 1) {\n segments.push(clampInt(Math.floor(Math.random() * max), 0, max - 1));\n }\n\n return segments;\n }\n}\n","type SpinHandler = () => Promise<void>;\n\nexport class SpinUiController {\n private readonly button: HTMLButtonElement | null;\n private readonly handler: SpinHandler;\n private attached = false;\n private spinning = false;\n\n constructor(button: HTMLButtonElement | null, handler: SpinHandler) {\n this.button = button;\n this.handler = handler;\n this.attach();\n }\n\n updateAvailability(hasQueue: boolean): void {\n if (!this.button) return;\n\n this.setDisabled(!hasQueue);\n }\n\n dispose(): void {\n if (this.button && this.attached) {\n this.button.removeEventListener('click', this.onClick);\n this.attached = false;\n }\n\n this.spinning = false;\n }\n\n private attach(): void {\n if (!this.button || this.attached) return;\n\n this.button.addEventListener('click', this.onClick, { passive: true });\n this.attached = true;\n }\n\n private setDisabled(disabled: boolean): void {\n if (!this.button) return;\n this.button.disabled = disabled;\n if (disabled) this.button.setAttribute('aria-busy', 'true');\n else this.button.removeAttribute('aria-busy');\n }\n\n private onClick = async (): Promise<void> => {\n if (this.spinning) return;\n\n this.spinning = true;\n this.setDisabled(true);\n\n try {\n await this.handler();\n } finally {\n this.spinning = false;\n this.setDisabled(false);\n }\n };\n}\n","export const reelVertexShader = `\n attribute vec2 a_pos;\n attribute vec2 a_uv;\n varying vec2 v_uv;\n void main() {\n v_uv = a_uv;\n gl_Position = vec4(a_pos, 0.0, 1.0);\n }\n`;\n\nexport const reelFragmentShader = `\n #ifdef GL_FRAGMENT_PRECISION_HIGH\n precision highp float;\n #else\n precision mediump float;\n #endif\n\n varying vec2 v_uv;\n uniform sampler2D u_tex;\n uniform float u_reelX;\n uniform float u_reelW;\n uniform float u_offsetPx;\n uniform float u_frameHeight;\n uniform float u_spriteHeight;\n uniform float u_visibleSlots;\n uniform float u_heightScale;\n uniform float u_edgePower;\n uniform float u_warpAngle;\n uniform float u_blurPx;\n\n const float HALF_PI = 1.5707963;\n\n float saturate(float x) { return clamp(x, 0.0, 1.0); }\n\n void main() {\n if (v_uv.x < u_reelX || v_uv.x > (u_reelX + u_reelW)) {\n discard;\n }\n\n float localX = (v_uv.x - u_reelX) / u_reelW;\n float localY = v_uv.y;\n float hs = max(0.0001, u_heightScale);\n float fullY = localY * hs + (1.0 - hs) * 0.5;\n\n float warp = max(0.0, u_warpAngle);\n float warpStrength = saturate(warp / HALF_PI);\n float distNorm = abs(fullY - 0.5) * 2.0;\n float edgePow = max(0.5, u_edgePower);\n float edgeWeight = pow(saturate(distNorm), edgePow) * warpStrength;\n\n float stretchMax = 6.0;\n float stretch = mix(1.0, stretchMax, edgeWeight);\n float warpedY = 0.5 + (fullY - 0.5) * stretch;\n\n float arcY = 0.5 + 0.5 * sin((warpedY - 0.5) * HALF_PI);\n warpedY = mix(warpedY, arcY, 0.2);\n\n float yPx = u_offsetPx + warpedY * (u_frameHeight * u_visibleSlots);\n float v = fract((u_offsetPx / u_spriteHeight) +\n warpedY * ((u_frameHeight * u_visibleSlots) / u_spriteHeight));\n\n vec4 base = texture2D(u_tex, vec2(localX, v));\n\n if (u_blurPx > 0.0) {\n float maxStepPx = 1.0 + 3.0 * saturate(u_blurPx / 4.0);\n float blurPx = min(u_blurPx, maxStepPx);\n float blurStep = blurPx / max(1.0, u_spriteHeight);\n vec4 sum = base * 0.227027;\n sum += texture2D(u_tex, vec2(localX, fract(v + blurStep))) * 0.1945946;\n sum += texture2D(u_tex, vec2(localX, fract(v - blurStep))) * 0.1945946;\n sum += texture2D(u_tex, vec2(localX, fract(v + 2.0 * blurStep))) * 0.1216216;\n sum += texture2D(u_tex, vec2(localX, fract(v - 2.0 * blurStep))) * 0.1216216;\n sum += texture2D(u_tex, vec2(localX, fract(v + 3.0 * blurStep))) * 0.054054;\n sum += texture2D(u_tex, vec2(localX, fract(v - 3.0 * blurStep))) * 0.054054;\n sum += texture2D(u_tex, vec2(localX, fract(v + 4.0 * blurStep))) * 0.016216;\n sum += texture2D(u_tex, vec2(localX, fract(v - 4.0 * blurStep))) * 0.016216;\n base = sum;\n }\n\n gl_FragColor = base;\n }\n`;\n\nexport const lineVertexShader = `\n attribute vec2 a_pos;\n varying vec2 v_pos;\n void main() {\n v_pos = a_pos;\n gl_Position = vec4(a_pos, 0.0, 1.0);\n }\n`;\n\nexport const lineFragmentShader = `\n #ifdef GL_FRAGMENT_PRECISION_HIGH\n precision highp float;\n #else\n precision mediump float;\n #endif\n\n varying vec2 v_pos;\n uniform float u_time;\n uniform vec2 u_resolution;\n uniform float u_thickness;\n uniform float u_intensity;\n\n void main() {\n vec2 uv = v_pos;\n float centerY = 0.0;\n float distY = abs(uv.y - centerY);\n float thicknessPx = u_thickness / u_resolution.y * 2.0;\n\n float lineMask = 1.0 - smoothstep(0.0, thicknessPx * 2.0, distY);\n\n if (lineMask <= 0.001) {\n discard;\n }\n\n vec3 color;\n float alpha = lineMask * u_intensity;\n\n float warm = 1.0 - exp(-u_time * 2.6);\n float flicker = (sin(u_time * 37.0) * sin(u_time * 13.0)) * 0.28;\n flicker *= exp(-u_time * 2.2);\n warm = clamp(warm + flicker, 0.0, 1.0);\n float warmColor = mix(0.65, 1.0, warm);\n alpha *= warm;\n\n float pulse = sin(u_time * 4.0) * 0.3 + 0.7;\n float x = (uv.x + 1.0) * 0.5;\n float wave = sin(x * 30.0 - u_time * 8.0) * 0.2;\n\n color = vec3(1.0, 0.8, 0.2) * (pulse + wave);\n\n float core = exp(-distY * 120.0 / thicknessPx);\n color = mix(color, vec3(1.0), core * 0.6);\n\n color *= warmColor;\n gl_FragColor = vec4(color, alpha);\n }\n`;\n","import type { ReelDealFinalSpinLine } from '../types';\nimport { lineFragmentShader, lineVertexShader } from './shaders';\n\ntype Gl = WebGLRenderingContext | WebGL2RenderingContext;\n\ntype Uniforms = {\n time: WebGLUniformLocation | null;\n resolution: WebGLUniformLocation | null;\n thickness: WebGLUniformLocation | null;\n intensity: WebGLUniformLocation | null;\n};\n\ntype Attribs = { pos: number };\n\nexport class LineRenderer {\n private program: WebGLProgram | null = null;\n private buffer: WebGLBuffer | null = null;\n private attribs: Attribs | null = null;\n private uniforms: Uniforms | null = null;\n private startTime: number | null = null;\n private show = false;\n\n init(gl: Gl, _finalSpinLine: ReelDealFinalSpinLine): void {\n const program = this.createProgram(gl, lineVertexShader, lineFragmentShader);\n\n if (!program) return;\n\n const buffer = gl.createBuffer();\n\n if (!buffer) {\n gl.deleteProgram(program);\n\n return;\n }\n\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n\n const quad = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]);\n\n gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n\n this.program = program;\n this.buffer = buffer;\n this.attribs = { pos: gl.getAttribLocation(program, 'a_pos') };\n this.uniforms = {\n time: gl.getUniformLocation(program, 'u_time'),\n resolution: gl.getUniformLocation(program, 'u_resolution'),\n thickness: gl.getUniformLocation(program, 'u_thickness'),\n intensity: gl.getUniformLocation(program, 'u_intensity'),\n };\n this.startTime = null;\n this.show = false;\n }\n\n showLine(): void {\n this.show = true;\n this.startTime = null;\n }\n\n hideLine(): void {\n this.show = false;\n this.startTime = null;\n }\n\n dispose(gl: Gl): void {\n if (this.program) gl.deleteProgram(this.program);\n if (this.buffer) gl.deleteBuffer(this.buffer);\n\n this.program = null;\n this.buffer = null;\n this.attribs = null;\n this.uniforms = null;\n this.startTime = null;\n this.show = false;\n }\n\n render(\n gl: Gl,\n canvas: HTMLCanvasElement,\n finalSpinLine: ReelDealFinalSpinLine,\n dpr: number,\n ): void {\n if (!this.show || !this.program || !this.buffer || !this.attribs || !this.uniforms) return;\n\n const now = performance.now();\n\n if (this.startTime === null) this.startTime = now;\n\n const elapsed = (now - this.startTime) / 1000;\n\n gl.useProgram(this.program);\n gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);\n\n gl.enableVertexAttribArray(this.attribs.pos);\n gl.vertexAttribPointer(this.attribs.pos, 2, gl.FLOAT, false, 0, 0);\n\n gl.uniform1f(this.uniforms.time, elapsed);\n gl.uniform2f(this.uniforms.resolution, canvas.width, canvas.height);\n gl.uniform1f(this.uniforms.thickness, finalSpinLine.thickness * dpr);\n gl.uniform1f(this.uniforms.intensity, finalSpinLine.intensity);\n\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n }\n\n private createProgram(gl: Gl, vsSource: string, fsSource: string): WebGLProgram | null {\n const vs = this.createShader(gl, gl.VERTEX_SHADER, vsSource);\n const fs = this.createShader(gl, gl.FRAGMENT_SHADER, fsSource);\n\n if (!vs || !fs) {\n if (vs) gl.deleteShader(vs);\n if (fs) gl.deleteShader(fs);\n\n return null;\n }\n\n const program = gl.createProgram();\n\n if (!program) return null;\n\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n gl.deleteProgram(program);\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n\n return null;\n }\n\n gl.detachShader(program, vs);\n gl.detachShader(program, fs);\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n\n return program;\n }\n\n private createShader(gl: Gl, type: number, source: string): WebGLShader | null {\n const shader = gl.createShader(type);\n\n if (!shader) return null;\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n gl.deleteShader(shader);\n\n return null;\n }\n\n return shader;\n }\n}\n","import type { ReelDealFinalSpinLine, ReelDealSpinBlur, ReelDealWebGLShading } from '../types';\nimport { clamp01, mod } from '../utils/math';\nimport { LineRenderer } from './lineRenderer';\nimport { reelFragmentShader, reelVertexShader } from './shaders';\n\nexport type RendererInit = {\n canvas: HTMLCanvasElement;\n spriteImg: HTMLImageElement;\n spriteWidth: number;\n spriteHeight: number;\n frameHeight: number;\n reels: number;\n visibleSlots: number;\n heightScale: number;\n spinBlur: ReelDealSpinBlur;\n webglShading: ReelDealWebGLShading;\n finalSpinLine: ReelDealFinalSpinLine | null;\n};\n\nexport type RenderFrame = {\n offsets: ArrayLike<number>;\n velocities: ArrayLike<number>;\n dpr: number;\n};\n\nexport interface Renderer {\n init(init: RendererInit): void;\n render(frame: RenderFrame): void;\n onResize(width: number, height: number): void;\n showFinalLine(): void;\n hideFinalLine(): void;\n dispose(): void;\n getError(): string | null;\n}\n\ntype WebGLAttribs = { pos: number; uv: number };\ntype WebGLUniforms = {\n texture: WebGLUniformLocation | null;\n reelX: WebGLUniformLocation | null;\n reelW: WebGLUniformLocation | null;\n offsetPx: WebGLUniformLocation | null;\n frameHeight: WebGLUniformLocation | null;\n spriteHeight: WebGLUniformLocation | null;\n visibleSlots: WebGLUniformLocation | null;\n heightScale: WebGLUniformLocation | null;\n edgePower: WebGLUniformLocation | null;\n warpAngle: WebGLUniformLocation | null;\n blurPx: WebGLUniformLocation | null;\n};\n\nexport class WebGLRenderer implements Renderer {\n private gl: WebGLRenderingContext | WebGL2RenderingContext | null = null;\n private glProgram: WebGLProgram | null = null;\n private glBuffer: WebGLBuffer | null = null;\n private glTexture: WebGLTexture | null = null;\n private glAttribs: WebGLAttribs | null = null;\n private glUniforms: WebGLUniforms | null = null;\n\n private config: Omit<RendererInit, 'canvas' | 'spriteImg'> | null = null;\n private canvas: HTMLCanvasElement | null = null;\n private spriteImg: HTMLImageElement | null = null;\n private webglInitError: string | null = null;\n private lineRenderer: LineRenderer | null = null;\n private showLine = false;\n\n init(init: RendererInit): void {\n this.canvas = init.canvas;\n this.spriteImg = init.spriteImg;\n this.config = {\n spriteWidth: init.spriteWidth,\n spriteHeight: init.spriteHeight,\n frameHeight: init.frameHeight,\n reels: init.reels,\n visibleSlots: init.visibleSlots,\n heightScale: init.heightScale,\n spinBlur: init.spinBlur,\n webglShading: init.webglShading,\n finalSpinLine: init.finalSpinLine,\n };\n\n this.lineRenderer = init.finalSpinLine ? new LineRenderer() : null;\n\n this.tryInitWebGL();\n\n if (!this.gl) {\n throw new Error(\n `WebGL is not supported or failed to initialize. ${this.webglInitError ?? ''}`.trim(),\n );\n }\n }\n\n getError(): string | null {\n return this.webglInitError;\n }\n\n render(frame: RenderFrame): void {\n if (!this.gl || !this.config || !this.spriteImg) return;\n if (!this.glProgram || !this.glAttribs || !this.glUniforms || !this.glBuffer) return;\n\n this.renderWebGL(frame);\n this.renderFinalLine(frame);\n }\n\n onResize(width: number, height: number): void {\n if (!this.gl || !this.canvas) return;\n\n this.gl.viewport(0, 0, width, height);\n }\n\n showFinalLine(): void {\n if (!this.config?.finalSpinLine) return;\n\n this.showLine = true;\n this.lineRenderer?.showLine();\n }\n\n hideFinalLine(): void {\n this.showLine = false;\n this.lineRenderer?.hideLine();\n }\n\n dispose(): void {\n if (!this.gl) return;\n if (this.glTexture) this.gl.deleteTexture(this.glTexture);\n if (this.glBuffer) this.gl.deleteBuffer(this.glBuffer);\n if (this.glProgram) this.gl.deleteProgram(this.glProgram);\n if (this.lineRenderer) this.lineRenderer.dispose(this.gl);\n\n this.glTexture = null;\n this.glBuffer = null;\n this.glProgram = null;\n this.glAttribs = null;\n this.glUniforms = null;\n this.gl = null;\n this.webglInitError = null;\n }\n\n private tryInitWebGL(): void {\n if (!this.canvas || !this.spriteImg || !this.config) return;\n\n this.dispose();\n this.webglInitError = null;\n\n const gl =\n (this.canvas.getContext('webgl2', {\n antialias: true,\n alpha: true,\n }) as WebGL2RenderingContext | null) ??\n (this.canvas.getContext('webgl', {\n antialias: true,\n alpha: true,\n }) as WebGLRenderingContext | null) ??\n (this.canvas.getContext('experimental-webgl', {\n antialias: true,\n alpha: true,\n }) as WebGLRenderingContext | null);\n\n if (!gl) {\n const fallback = document.createElement('canvas');\n const canWebGL = typeof WebGLRenderingContext !== 'undefined';\n const fallbackGl =\n (fallback.getContext('webgl2') as WebGL2RenderingContext | null) ??\n (fallback.getContext('webgl') as WebGLRenderingContext | null);\n\n this.webglInitError = `context is null (browser WebGL=${canWebGL ? 'yes' : 'no'}, fallback canvas=${fallbackGl ? 'yes' : 'no'})`;\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n return;\n }\n\n const program = this.createWebGLProgram(gl, reelVertexShader, reelFragmentShader);\n\n if (!program) {\n this.webglInitError = 'shader program failed to compile/link (see console)';\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n return;\n }\n\n const buffer = gl.createBuffer();\n\n if (!buffer) {\n this.webglInitError = 'failed to create vertex buffer';\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n gl.deleteProgram(program);\n\n return;\n }\n\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n\n const quad = new Float32Array([-1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0, 1, 1, 1, 0]);\n\n gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n\n const texture = gl.createTexture();\n\n if (!texture) {\n this.webglInitError = 'failed to create texture';\n\n console.warn('ReelDeal WebGL:', this.webglInitError);\n\n gl.deleteBuffer(buffer);\n gl.deleteProgram(program);\n\n return;\n }\n\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.spriteImg);\n\n const isPowerOfTwo = (n: number): boolean => n > 0 && (n & (n - 1)) === 0;\n const pot = isPowerOfTwo(this.config.spriteWidth) && isPowerOfTwo(this.config.spriteHeight);\n\n if (pot) {\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);\n gl.generateMipmap(gl.TEXTURE_2D);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n } else {\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n }\n\n gl.useProgram(program);\n gl.clearColor(0, 0, 0, 0);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\n\n this.gl = gl;\n this.glProgram = program;\n this.glBuffer = buffer;\n this.glTexture = texture;\n this.glAttribs = {\n pos: gl.getAttribLocation(program, 'a_pos'),\n uv: gl.getAttribLocation(program, 'a_uv'),\n };\n this.glUniforms = {\n texture: gl.getUniformLocation(program, 'u_tex'),\n reelX: gl.getUniformLocation(program, 'u_reelX'),\n reelW: gl.getUniformLocation(program, 'u_reelW'),\n offsetPx: gl.getUniformLocation(program, 'u_offsetPx'),\n frameHeight: gl.getUniformLocation(program, 'u_frameHeight'),\n spriteHeight: gl.getUniformLocation(program, 'u_spriteHeight'),\n visibleSlots: gl.getUniformLocation(program, 'u_visibleSlots'),\n heightScale: gl.getUniformLocation(program, 'u_heightScale'),\n edgePower: gl.getUniformLocation(program, 'u_edgePower'),\n warpAngle: gl.getUniformLocation(program, 'u_warpAngle'),\n blurPx: gl.getUniformLocation(program, 'u_blurPx'),\n };\n\n if (this.config.finalSpinLine && this.lineRenderer) {\n this.lineRenderer.init(gl, this.config.finalSpinLine);\n }\n }\n\n private createWebGLProgram(\n gl: WebGLRenderingContext | WebGL2RenderingContext,\n vsSource: string,\n fsSource: string,\n ): WebGLProgram | null {\n const vs = this.createWebGLShader(gl, gl.VERTEX_SHADER, vsSource);\n const fs = this.createWebGLShader(gl, gl.FRAGMENT_SHADER, fsSource);\n\n if (!vs || !fs) {\n if (vs) gl.deleteShader(vs);\n if (fs) gl.deleteShader(fs);\n\n return null;\n }\n\n const program = gl.createProgram();\n\n if (!program) return null;\n\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const info = gl.getProgramInfoLog(program) ?? 'unknown link error';\n\n this.webglInitError = `program link failed: ${info}`;\n\n console.warn('ReelDeal WebGL: program link failed', info);\n\n gl.deleteProgram(program);\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n\n return null;\n }\n\n gl.detachShader(program, vs);\n gl.detachShader(program, fs);\n gl.deleteShader(vs);\n gl.deleteShader(fs);\n\n return program;\n }\n\n private createWebGLShader(\n gl: WebGLRenderingContext | WebGL2RenderingContext,\n type: number,\n source: string,\n ): WebGLShader | null {\n const shader = gl.createShader(type);\n\n if (!shader) return null;\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n const info = gl.getShaderInfoLog(shader) ?? 'unknown compile error';\n\n this.webglInitError = `shader compile failed: ${info}`;\n\n console.warn('ReelDeal WebGL: shader compile failed', info);\n\n gl.deleteShader(shader);\n\n return null;\n }\n\n return shader;\n }\n\n private getSpinBlurPxForReel(reel: number, velocities: ArrayLike<number>): number {\n if (!this.config) return 0;\n\n const speed = Math.abs(velocities[reel] ?? 0);\n const speedAtMax = Math.max(1, this.config.spinBlur.speedAtMax);\n const t = clamp01(speed / speedAtMax);\n\n return t * Math.max(0, this.config.spinBlur.maxPx);\n }\n\n private renderWebGL(frame: RenderFrame): void {\n if (\n !this.gl ||\n !this.glProgram ||\n !this.glAttribs ||\n !this.glUniforms ||\n !this.glTexture ||\n !this.glBuffer ||\n !this.config\n ) {\n return;\n }\n\n if (!this.spriteImg || !this.canvas) return;\n\n const gl = this.gl;\n\n gl.useProgram(this.glProgram);\n gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffer);\n\n gl.enableVertexAttribArray(this.glAttribs.pos);\n gl.vertexAttribPointer(this.glAttribs.pos, 2, gl.FLOAT, false, 16, 0);\n gl.enableVertexAttribArray(this.glAttribs.uv);\n gl.vertexAttribPointer(this.glAttribs.uv, 2, gl.FLOAT, false, 16, 8);\n\n gl.activeTexture(gl.TEXTURE0);\n gl.bindTexture(gl.TEXTURE_2D, this.glTexture);\n gl.uniform1i(this.glUniforms.texture, 0);\n\n gl.uniform1f(this.glUniforms.frameHeight, this.config.frameHeight);\n gl.uniform1f(this.glUniforms.spriteHeight, this.config.spriteHeight);\n gl.uniform1f(this.glUniforms.visibleSlots, this.config.visibleSlots);\n gl.uniform1f(this.glUniforms.heightScale, this.config.heightScale);\n gl.uniform1f(this.glUniforms.edgePower, Math.max(0.5, this.config.webglShading.edgePower));\n\n const shading = this.config.webglShading;\n const warpAngle = (Math.max(0, shading.warpAngleDeg) * Math.PI) / 180;\n\n gl.uniform1f(this.glUniforms.warpAngle, warpAngle);\n gl.clear(gl.COLOR_BUFFER_BIT);\n\n const reelW = 1 / this.config.reels;\n\n for (let r = 0; r < this.config.reels; r += 1) {\n gl.uniform1f(this.glUniforms.reelX, r * reelW);\n gl.uniform1f(this.glUniforms.reelW, reelW);\n\n const offsetPx = mod(frame.offsets[r], this.config.spriteHeight);\n\n gl.uniform1f(this.glUniforms.offsetPx, offsetPx);\n gl.uniform1f(this.glUniforms.blurPx, this.getSpinBlurPxForReel(r, frame.velocities));\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n }\n }\n\n private renderFinalLine(frame: RenderFrame): void {\n if (!this.showLine || !this.config?.finalSpinLine) return;\n if (!this.gl || !this.canvas || !this.lineRenderer) return;\n\n this.lineRenderer.render(this.gl, this.canvas, this.config.finalSpinLine, frame.dpr);\n }\n}\n","const resolveElementById = <T extends HTMLElement>(id: string, kind: string): T => {\n const el = document.getElementById(id);\n\n if (!el) {\n throw new Error(`${kind} not found: ${id}`);\n }\n\n return el as T;\n};\n\nexport const resolveElement = <T extends HTMLElement>(elOrId: T | string, kind: string): T => {\n if (typeof elOrId === 'string') return resolveElementById<T>(elOrId, kind);\n\n return elOrId;\n};\n","import { loadSprite } from './assets/spriteLoader';\nimport {\n buildSpinPlan,\n getStopSpringOffset,\n updateIdleOffsets,\n updateVelocities,\n} from './core/animation';\nimport { RafLoop } from './core/loop';\nimport { SpinQueueController } from './core/spinQueue';\nimport { SpinUiController } from './core/uiControls';\nimport {\n normalizeFinalSpinLine,\n normalizeIdleBob,\n normalizeMaxDpr,\n normalizeSpinBlur,\n normalizeSpinConfig,\n normalizeWebGLShading,\n} from './normalize';\nimport { WebGLRenderer } from './render/webglRenderer';\nimport type {\n ReelDealFinalSpinLine,\n ReelDealIdleBob,\n ReelDealOptions,\n ReelDealSpinBlur,\n ReelDealSpinConfig,\n ReelDealSpinRequest,\n ReelDealSpinState,\n ReelDealSpriteSource,\n ReelDealWebGLShading,\n} from './types';\nimport { resolveElement } from './utils/dom';\nimport { clamp01, clampInt, reelProgress } from './utils/math';\n\nconst MAX_REELS = 5;\nconst SPIN_TICK_START = 0.72;\nconst SPIN_TICK_RANGE = 0.28;\nconst SPIN_TICK_MAX_PX = 7;\nconst STOP_SPRING_OVERSHOOT_PX = 55;\nconst STOP_SPRING_SETTLE_MS = 300;\nconst STOP_SPRING_MAX_RATIO = 0.35;\n\ntype SpinAnimation = {\n startTime: number;\n startOffsets: number[];\n deltas: number[];\n durationMs: number[];\n settleDurationMs: number[];\n targets: number[];\n};\n\ntype RequiredOptions = ReelDealOptions & {\n canvas: HTMLCanvasElement | string;\n container: HTMLElement | string;\n sprite: ReelDealSpriteSource;\n slotCount: number;\n};\n\nexport class ReelDeal {\n private readonly canvas: HTMLCanvasElement;\n private readonly container: HTMLElement;\n\n private readonly opts: RequiredOptions;\n private readonly visibleSlots = 3;\n private readonly heightScale = 2 / 3;\n private readonly centerIndex = 1;\n private readonly reels: number;\n private readonly spinConfig: ReturnType<typeof normalizeSpinConfig>;\n private readonly spinBlur: ReelDealSpinBlur;\n private readonly webglShading: ReelDealWebGLShading;\n private readonly idleBob: ReelDealIdleBob | null;\n private readonly maxDpr: number;\n private readonly finalSpinLine: ReelDealFinalSpinLine | null;\n\n private spriteImg: HTMLImageElement | null = null;\n private spriteWidth = 0;\n private spriteHeight = 0;\n private frameHeight = 0;\n\n private width = 0;\n private height = 0;\n private dpr = 1;\n\n private readonly offsets: Float32Array;\n private readonly velocities: Float32Array;\n private lastVelT: number | null = null;\n private readonly lastVelOffsets: Float32Array;\n private idleStartTime: number | null = null;\n private readonly idleBaseOffsets: Float32Array;\n\n private renderer: WebGLRenderer | null = null;\n private webglInitError: string | null = null;\n\n private anim: SpinAnimation | null = null;\n private readonly loop = new RafLoop();\n private pendingSpinResolvers: Array<{\n resolve: () => void;\n reject: (error: unknown) => void;\n cleanup: () => void;\n }> = [];\n\n private showFinalLine = false;\n private isLastSpin = false;\n\n private resizeObserver: ResizeObserver | null = null;\n private resizeQueued = false;\n\n private button: HTMLButtonElement | null = null;\n private uiController: SpinUiController | null = null;\n private readonly spinQueueController: SpinQueueController;\n\n constructor(options: ReelDealOptions) {\n if (!options.canvas) throw new Error('ReelDeal: canvas is required');\n if (!options.container) throw new Error('ReelDeal: container is required');\n if (!options.sprite) throw new Error('ReelDeal: sprite is required');\n if (options.slotCount === undefined) throw new Error('ReelDeal: slotCount is required');\n\n const required: RequiredOptions = {\n ...options,\n canvas: options.canvas,\n container: options.container,\n sprite: options.sprite,\n slotCount: clampInt(options.slotCount, 1, Number.MAX_SAFE_INTEGER),\n };\n\n this.opts = required;\n this.reels = clampInt(options.reels ?? 1, 1, MAX_REELS);\n this.spinConfig = normalizeSpinConfig(options.spinConfig);\n this.spinBlur = normalizeSpinBlur(options.spinBlur);\n this.webglShading = normalizeWebGLShading(options.webglShading);\n this.idleBob = normalizeIdleBob(options.idleBob);\n this.maxDpr = normalizeMaxDpr(options.maxDpr);\n this.finalSpinLine = normalizeFinalSpinLine(options.finalSpinLine);\n\n this.canvas = resolveElement<HTMLCanvasElement>(required.canvas, 'Canvas');\n this.container = resolveElement<HTMLElement>(required.container, 'Container');\n\n this.offsets = new Float32Array(this.reels);\n this.velocities = new Float32Array(this.reels);\n this.lastVelOffsets = new Float32Array(this.reels);\n this.idleBaseOffsets = new Float32Array(this.reels);\n\n this.spinQueueController = new SpinQueueController({\n slotCount: required.slotCount,\n reels: this.reels,\n initialQueue: required.queuedSpinStates,\n });\n }\n\n private initRenderer(): void {\n if (!this.spriteImg) return;\n\n const renderer = new WebGLRenderer();\n\n try {\n renderer.init({\n canvas: this.canvas,\n spriteImg: this.spriteImg,\n spriteWidth: this.spriteWidth,\n spriteHeight: this.spriteHeight,\n frameHeight: this.frameHeight,\n reels: this.reels,\n visibleSlots: this.visibleSlots,\n heightScale: this.heightScale,\n spinBlur: this.spinBlur,\n webglShading: this.webglShading,\n finalSpinLine: this.finalSpinLine,\n });\n this.webglInitError = renderer.getError();\n this.renderer = renderer;\n } catch (error) {\n this.webglInitError =\n renderer.getError() ?? (error instanceof Error ? error.message : String(error));\n throw error;\n }\n }\n\n async init(): Promise<void> {\n const sprite = await loadSprite(this.opts.sprite, this.opts.slotCount);\n\n this.spriteImg = sprite.image;\n this.spriteWidth = sprite.spriteWidth;\n this.frameHeight = sprite.frameHeight;\n this.spriteHeight = sprite.spriteHeight;\n\n this.initRenderer();\n\n if (!this.renderer) {\n throw new Error(\n `WebGL is not supported or failed to initialize. ${this.webglInitError ?? ''}`.trim(),\n );\n }\n\n this.syncContainerLayoutVars();\n this.ensureCanvasCoversContainer();\n this.setupResizeWatcher();\n this.resizeToContainer();\n\n this.setSegments(this.opts.initialSegments);\n\n this.button = this.resolveButton();\n this.uiController = new SpinUiController(this.button, this.handleButtonClick);\n this.uiController.updateAvailability(true);\n\n this.render();\n this.startLoop();\n }\n\n destroy(): void {\n this.stop();\n\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n\n this.uiController?.dispose();\n this.uiController = null;\n\n if (this.renderer) {\n this.renderer.dispose();\n this.renderer = null;\n }\n }\n\n private ensureCanvasCoversContainer(): void {\n if (this.canvas === this.container) return;\n if (!this.container.contains(this.canvas)) return;\n\n if (!this.canvas.style.width) this.canvas.style.width = '100%';\n if (!this.canvas.style.height) this.canvas.style.height = '100%';\n if (!this.canvas.style.display) this.canvas.style.display = 'block';\n }\n\n private syncContainerLayoutVars(): void {\n const target = this.canvas === this.container ? this.canvas : this.container;\n\n target.style.setProperty('--reels', String(this.reels));\n target.style.setProperty('--visible-slots', String(this.visibleSlots));\n target.style.aspectRatio = `${this.reels} / ${this.visibleSlots * this.heightScale}`;\n\n if (this.canvas !== this.container) {\n this.container.style.overflow = 'hidden';\n }\n }\n\n setSegments(initialSegments?: number[]): void {\n this.ensureReady();\n\n const segments = initialSegments ?? [];\n\n for (let r = 0; r < this.reels; r += 1) {\n const segIndex = segments[r] ?? 0;\n const idx = clampInt(segIndex, 0, this.opts.slotCount - 1);\n\n this.offsets[r] = idx * this.frameHeight - this.centerIndex * this.frameHeight;\n this.lastVelOffsets[r] = this.offsets[r];\n this.velocities[r] = 0;\n this.idleBaseOffsets[r] = this.offsets[r];\n }\n\n this.lastVelT = null;\n this.idleStartTime = null;\n this.anim = null;\n\n this.stop();\n this.render();\n this.startLoop();\n }\n\n setSegment(segmentIndex: number): void {\n this.setSegments(new Array(this.reels).fill(segmentIndex));\n }\n\n spinOnce(\n request: ReelDealSpinRequest | number[],\n configOverride?: Partial<ReelDealSpinConfig>,\n ): Promise<void> {\n this.ensureReady();\n\n const normalizedRequest: ReelDealSpinRequest = Array.isArray(request)\n ? { stopAtSegments: request, config: configOverride }\n : request;\n\n if (normalizedRequest.signal?.aborted) {\n return Promise.reject(\n normalizedRequest.signal.reason ?? new DOMException('Aborted', 'AbortError'),\n );\n }\n\n const spinConfig = this.getSpinConfig(normalizedRequest.config ?? configOverride);\n const useSpeed = spinConfig.speedPxS > 0;\n const stopSpringOvershootPx = this.getStopSpringOvershootPx();\n const stopSpringSettleMs = Math.max(0, STOP_SPRING_SETTLE_MS);\n const plan = buildSpinPlan({\n stopAtSegments: normalizedRequest.stopAtSegments,\n currentOffsets: this.offsets,\n reels: this.reels,\n frameHeight: this.frameHeight,\n spriteHeight: this.spriteHeight,\n centerIndex: this.centerIndex,\n slotCount: this.opts.slotCount,\n spinConfig,\n stopSpringOvershootPx,\n stopSpringSettleMs,\n useSpeed,\n });\n\n if (plan.totalDelta === 0 || (plan.allSpinZero && plan.allSettleZero)) {\n for (let r = 0; r < this.reels; r += 1) {\n this.offsets[r] = plan.targets[r];\n }\n\n this.anim = null;\n\n this.resolvePendingSpins();\n this.render();\n\n return Promise.resolve();\n }\n\n const startTime = performance.now();\n\n this.anim = {\n startTime,\n startOffsets: plan.startOffsets,\n deltas: plan.deltas,\n durationMs: plan.durations,\n settleDurationMs: plan.settleDurationMs,\n targets: plan.targets,\n };\n this.idleStartTime = null;\n\n this.startLoop();\n\n const abortController = new AbortController();\n\n const abortListener = (): void => {\n const reason =\n abortController.signal.reason ?? normalizedRequest.signal?.reason ?? 'Spin aborted';\n\n this.stop(reason);\n };\n\n const externalAbort = (): void => {\n abortController.abort(\n normalizedRequest.signal?.reason ?? new DOMException('Aborted', 'AbortError'),\n );\n };\n\n if (normalizedRequest.signal) {\n normalizedRequest.signal.addEventListener('abort', externalAbort, { once: true });\n }\n\n let timeoutId: number | null = null;\n\n if (normalizedRequest.timeoutMs && normalizedRequest.timeoutMs > 0) {\n timeoutId = window.setTimeout(() => {\n abortController.abort(new DOMException('Spin timed out', 'TimeoutError'));\n }, normalizedRequest.timeoutMs);\n }\n\n return new Promise((resolve, reject) => {\n const cleanup = (): void => {\n if (timeoutId !== null) window.clearTimeout(timeoutId);\n\n abortController.signal.removeEventListener('abort', abortListener);\n\n if (normalizedRequest.signal) {\n normalizedRequest.signal.removeEventListener('abort', externalAbort);\n }\n };\n\n if (abortController.signal.aborted) {\n cleanup();\n\n reject(abortController.signal.reason ?? new DOMException('Aborted', 'AbortError'));\n\n return;\n }\n\n abortController.signal.addEventListener('abort', abortListener, { once: true });\n this.pendingSpinResolvers.push({\n resolve: () => {\n cleanup();\n resolve();\n },\n reject: (error) => {\n cleanup();\n reject(error);\n },\n cleanup,\n });\n });\n }\n\n private getSpinConfig(\n overrides?: Partial<ReelDealSpinConfig>,\n ): ReturnType<typeof normalizeSpinConfig> {\n if (!overrides) return this.spinConfig;\n\n return normalizeSpinConfig({ ...this.spinConfig, ...overrides });\n }\n\n private getStopSpringOvershootPx(): number {\n const cap = this.frameHeight * STOP_SPRING_MAX_RATIO;\n const overshoot = Math.max(0, STOP_SPRING_OVERSHOOT_PX);\n\n return Math.min(overshoot, cap);\n }\n\n async spinQueue(spins: ReelDealSpinState[]): Promise<void> {\n this.spinQueueController.reset(spins);\n this.uiController?.updateAvailability(true);\n\n for (let i = 0; i < spins.length; i += 1) {\n const spin = spins[i];\n\n await this.spinOnce(spin.stopAtSegments);\n\n spin.callback?.(i, spin.stopAtSegments);\n }\n\n this.uiController?.updateAvailability(true);\n }\n\n stop(error?: unknown): void {\n this.loop.stop();\n this.anim = null;\n\n if (error !== undefined) {\n this.rejectPendingSpins(error);\n } else {\n this.resolvePendingSpins();\n }\n\n this.lastVelT = null;\n\n this.velocities.fill(0);\n }\n\n private resolvePendingSpins(): void {\n if (this.pendingSpinResolvers.length === 0) return;\n\n const resolvers = this.pendingSpinResolvers;\n\n this.pendingSpinResolvers = [];\n\n for (const { resolve, cleanup } of resolvers) {\n cleanup();\n resolve();\n }\n }\n\n private rejectPendingSpins(error: unknown): void {\n if (this.pendingSpinResolvers.length === 0) return;\n\n const resolvers = this.pendingSpinResolvers;\n\n this.pendingSpinResolvers = [];\n\n for (const { reject, cleanup } of resolvers) {\n cleanup();\n reject(error);\n }\n }\n\n requestResize(): void {\n this.queueResize();\n }\n\n private ensureReady(): void {\n if (\n !this.spriteImg ||\n this.spriteWidth <= 0 ||\n this.spriteHeight <= 0 ||\n this.frameHeight <= 0\n ) {\n throw new Error('Sprite is not ready. Call init() and await it first.');\n }\n }\n\n private handleButtonClick = async (): Promise<void> => {\n this.hideFinalLine();\n\n const entry = this.spinQueueController.consume();\n\n this.isLastSpin = entry.isLast;\n this.uiController?.updateAvailability(true);\n\n await this.spinOnce(entry.state.stopAtSegments);\n\n if (this.isLastSpin && this.finalSpinLine) {\n this.showFinalLineEffect();\n }\n };\n\n private resolveButton(): HTMLButtonElement | null {\n if (!this.opts.button) return null;\n\n return resolveElement<HTMLButtonElement>(this.opts.button, 'Button');\n }\n\n private shouldAnimate(): boolean {\n return this.anim !== null || this.idleBob !== null || this.showFinalLine;\n }\n\n private startLoop(): void {\n if (this.loop.isRunning()) return;\n if (!this.shouldAnimate()) return;\n\n this.loop.start(this.tick);\n }\n\n private tick = (now: number): boolean => {\n if (this.anim) {\n const { startTime, durationMs, startOffsets, deltas, settleDurationMs, targets } = this.anim;\n const elapsed = now - startTime;\n\n let allDone = true;\n\n for (let r = 0; r < this.reels; r += 1) {\n const duration = durationMs[r] ?? 0;\n const settleDuration = settleDurationMs[r] ?? 0;\n const targetOffset = targets[r] ?? startOffsets[r] + deltas[r];\n\n if (duration > 0 && elapsed < duration) {\n const t = Math.min(1, elapsed / duration);\n const eased = reelProgress(t);\n\n if (t < 1) allDone = false;\n\n const baseOffset = startOffsets[r] + deltas[r] * eased;\n\n let tick = 0;\n\n if (t > SPIN_TICK_START && this.frameHeight > 0) {\n const u = clamp01((t - SPIN_TICK_START) / SPIN_TICK_RANGE);\n const env = (1 - u) ** 2;\n const phase = (baseOffset / this.frameHeight) * Math.PI * 2;\n const amp = Math.min(SPIN_TICK_MAX_PX, this.frameHeight * 0.06);\n\n tick = Math.sin(phase) * env * amp;\n }\n\n this.offsets[r] = baseOffset + tick;\n } else if (settleDuration > 0) {\n const t =\n duration <= 0 ? elapsed / settleDuration : (elapsed - duration) / settleDuration;\n const u = clamp01(t);\n const overshoot = startOffsets[r] + deltas[r] - targetOffset;\n const spring = getStopSpringOffset(overshoot, u);\n\n if (u < 1) allDone = false;\n\n this.offsets[r] = targetOffset + spring;\n } else {\n this.offsets[r] = targetOffset;\n }\n }\n\n this.updateVelocity(now);\n this.render();\n\n if (allDone) {\n for (let r = 0; r < this.reels; r += 1) {\n const targetOffset =\n this.anim.targets[r] ?? this.anim.startOffsets[r] + this.anim.deltas[r];\n\n this.offsets[r] = targetOffset;\n this.idleBaseOffsets[r] = this.offsets[r];\n }\n\n this.updateVelocity(now);\n\n for (let r = 0; r < this.reels; r += 1) {\n this.velocities[r] = 0;\n }\n\n this.anim = null;\n this.idleStartTime = null;\n\n this.render();\n this.resolvePendingSpins();\n }\n } else if (this.idleBob) {\n this.updateIdle(now);\n this.render();\n }\n\n return this.shouldAnimate();\n };\n\n private render(): void {\n if (!this.spriteImg) return;\n if (this.width <= 0 || this.height <= 0) return;\n if (!this.renderer) return;\n\n this.renderer.render({\n offsets: this.offsets,\n velocities: this.velocities,\n dpr: this.dpr,\n });\n }\n\n private updateVelocity(now: number): void {\n this.lastVelT = updateVelocities(\n now,\n {\n lastVelT: this.lastVelT,\n lastVelOffsets: this.lastVelOffsets,\n velocities: this.velocities,\n },\n this.offsets,\n );\n }\n\n private updateIdle(now: number): void {\n if (!this.idleBob) return;\n\n this.idleStartTime = updateIdleOffsets(\n now,\n {\n idleStartTime: this.idleStartTime,\n idleBaseOffsets: this.idleBaseOffsets,\n velocities: this.velocities,\n },\n this.idleBob,\n this.offsets,\n this.reels,\n );\n }\n\n private setupResizeWatcher(): void {\n if (typeof ResizeObserver === 'undefined') return;\n if (this.resizeObserver) return;\n\n this.resizeObserver = new ResizeObserver(() => this.queueResize());\n this.resizeObserver.observe(this.container);\n }\n\n private queueResize = (): void => {\n if (this.resizeQueued) return;\n\n this.resizeQueued = true;\n\n requestAnimationFrame(() => {\n this.resizeQueued = false;\n this.resizeToContainer();\n });\n };\n\n private resizeToContainer(): void {\n const cssWidth = this.container.clientWidth;\n const cssHeight = this.container.clientHeight;\n const slotsViewport = this.visibleSlots * this.heightScale;\n\n if (cssWidth <= 0 || cssHeight <= 0) {\n this.applyResize(1, 1);\n\n return;\n }\n\n const slots = slotsViewport;\n const reels = this.reels;\n const fitByWidthH = (cssWidth / reels) * slots;\n\n let w = cssWidth;\n let h = fitByWidthH;\n\n if (h > cssHeight) {\n h = cssHeight;\n w = (cssHeight / slots) * reels;\n }\n\n const viewportWidth = Math.max(1, Math.floor(w));\n const viewportHeight = Math.max(1, Math.floor(h));\n\n this.applyResize(viewportWidth, viewportHeight);\n this.render();\n }\n\n private applyViewportCrop(): void {\n this.ensureCanvasCoversContainer();\n this.canvas.style.transform = '';\n }\n\n private applyResize(width: number, height: number): void {\n const normalizedWidth = Math.max(1, Math.floor(width));\n const normalizedHeight = Math.max(1, Math.floor(height));\n\n const dpr = this.getTargetDpr();\n\n this.width = normalizedWidth;\n this.height = normalizedHeight;\n this.dpr = dpr;\n\n this.canvas.width = Math.floor(this.width * this.dpr);\n this.canvas.height = Math.floor(this.height * this.dpr);\n\n if (this.renderer) {\n this.renderer.onResize(this.canvas.width, this.canvas.height);\n }\n\n this.applyViewportCrop();\n }\n\n private getTargetDpr(): number {\n const rawDpr = window.devicePixelRatio || 1;\n const base = Math.max(1, rawDpr);\n\n return Math.min(this.maxDpr, base);\n }\n\n hideFinalLine(): void {\n this.showFinalLine = false;\n this.renderer?.hideFinalLine();\n\n this.render();\n }\n\n showFinalLineEffect(): void {\n if (!this.finalSpinLine) return;\n\n this.showFinalLine = true;\n this.renderer?.showFinalLine();\n\n this.startLoop();\n }\n}\n"],"names":["waitImageLoad","img","resolve","reject","onLoad","onError","loadSprite","sprite","slotCountRaw","slotCount","spriteWidth","frameHeight","expectedSpriteHeight","naturalHeight","defaultSpinConfig","defaultSpinBlur","defaultIdleBob","defaultWebglShading","defaultFinalSpinLine","SPIN_ACCEL_END","SPIN_DECEL_START","clampInt","value","min","max","mod","n","m","clamp01","t","rampHermite","u","x","brakeHermite","reelProgress","tt","finiteOr","fallback","nonNegative","nonNegativeInt","DEFAULT_MAX_DPR","normalizeSpinConfig","overrides","base","normalizeSpinBlur","normalizeIdleBob","normalizeMaxDpr","normalizeWebGLShading","normalizeFinalSpinLine","buildSpinPlan","input","stopAtSegments","currentOffsets","reels","spriteHeight","centerIndex","spinConfig","stopSpringOvershootPx","stopSpringSettleMs","useSpeed","startOffsets","deltas","durations","settleDurationMs","targets","minSpins","staggerMs","speedPxS","durationMs","stopSpringEnabled","r","desiredTop","desired","current","alignDelta","delta","desiredDistance","loops","extraDelta","extraLoops","finalDelta","settleDuration","overshoot","totalDelta","acc","v","allSpinZero","entry","allSettleZero","getStopSpringOffset","decay","angle","updateVelocities","now","state","offsets","dt","VELOCITY_DECAY","VELOCITY_GAIN","updateIdleOffsets","idleState","idleBob","amplitudePx","speedHz","phaseOffsetRad","rampMs","elapsedMs","tSec","rampT","ramp","omega","phase","RafLoop","step","signal","onAbort","SpinQueueController","config","queue","segments","SpinUiController","button","handler","hasQueue","disabled","reelVertexShader","reelFragmentShader","lineVertexShader","lineFragmentShader","LineRenderer","gl","_finalSpinLine","program","buffer","quad","canvas","finalSpinLine","dpr","elapsed","vsSource","fsSource","vs","fs","type","source","shader","WebGLRenderer","init","frame","width","height","canWebGL","fallbackGl","texture","isPowerOfTwo","info","reel","velocities","speed","speedAtMax","shading","warpAngle","reelW","offsetPx","resolveElementById","id","kind","el","resolveElement","elOrId","MAX_REELS","SPIN_TICK_START","SPIN_TICK_RANGE","SPIN_TICK_MAX_PX","STOP_SPRING_OVERSHOOT_PX","STOP_SPRING_SETTLE_MS","STOP_SPRING_MAX_RATIO","ReelDeal","options","required","renderer","error","target","initialSegments","segIndex","idx","segmentIndex","request","configOverride","normalizedRequest","plan","startTime","abortController","abortListener","reason","externalAbort","timeoutId","cleanup","cap","spins","i","spin","resolvers","allDone","duration","targetOffset","eased","baseOffset","tick","env","amp","spring","cssWidth","cssHeight","slotsViewport","slots","fitByWidthH","w","h","viewportWidth","viewportHeight","normalizedWidth","normalizedHeight","rawDpr"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,6 +4,30 @@ export declare const defaultSpinBlur: ReelDealSpinBlur;
4
4
 
5
5
  export declare const defaultSpinConfig: ReelDealSpinConfig;
6
6
 
7
+ declare type LoadedSprite = {
8
+ image: HTMLImageElement;
9
+ spriteWidth: number;
10
+ spriteHeight: number;
11
+ frameHeight: number;
12
+ slotCount: number;
13
+ };
14
+
15
+ export declare const loadSprite: (sprite: ReelDealSpriteSource, slotCountRaw: number) => Promise<LoadedSprite>;
16
+
17
+ declare type NormalizedSpinConfig = Required<ReelDealSpinConfig>;
18
+
19
+ export declare const normalizeFinalSpinLine: (overrides?: Partial<ReelDealFinalSpinLine> | false) => ReelDealFinalSpinLine | null;
20
+
21
+ export declare const normalizeIdleBob: (overrides?: Partial<ReelDealIdleBob> | false) => ReelDealIdleBob | null;
22
+
23
+ export declare const normalizeMaxDpr: (value?: number) => number;
24
+
25
+ export declare const normalizeSpinBlur: (overrides?: Partial<ReelDealSpinBlur>) => ReelDealSpinBlur;
26
+
27
+ export declare const normalizeSpinConfig: (overrides?: Partial<ReelDealSpinConfig>) => NormalizedSpinConfig;
28
+
29
+ export declare const normalizeWebGLShading: (overrides?: Partial<ReelDealWebGLShading>) => ReelDealWebGLShading;
30
+
7
31
  export declare class ReelDeal {
8
32
  private readonly canvas;
9
33
  private readonly container;
@@ -17,6 +41,7 @@ export declare class ReelDeal {
17
41
  private readonly webglShading;
18
42
  private readonly idleBob;
19
43
  private readonly maxDpr;
44
+ private readonly finalSpinLine;
20
45
  private spriteImg;
21
46
  private spriteWidth;
22
47
  private spriteHeight;
@@ -24,70 +49,62 @@ export declare class ReelDeal {
24
49
  private width;
25
50
  private height;
26
51
  private dpr;
27
- private offsets;
28
- private velocities;
52
+ private readonly offsets;
53
+ private readonly velocities;
29
54
  private lastVelT;
30
- private lastVelOffsets;
55
+ private readonly lastVelOffsets;
31
56
  private idleStartTime;
32
- private idleBaseOffsets;
33
- private gl;
34
- private glProgram;
35
- private glBuffer;
36
- private glTexture;
37
- private glAttribs;
57
+ private readonly idleBaseOffsets;
58
+ private renderer;
38
59
  private webglInitError;
39
- private glUniforms;
40
60
  private anim;
41
- private rafId;
61
+ private readonly loop;
42
62
  private pendingSpinResolvers;
63
+ private showFinalLine;
64
+ private isLastSpin;
43
65
  private resizeObserver;
44
66
  private resizeQueued;
45
67
  private button;
46
- private buttonHandlerAttached;
47
- private spinning;
48
- private queuedSpins;
68
+ private uiController;
69
+ private readonly spinQueueController;
49
70
  constructor(options: ReelDealOptions);
71
+ private initRenderer;
50
72
  init(): Promise<void>;
51
73
  destroy(): void;
52
74
  private ensureCanvasCoversContainer;
53
75
  private syncContainerLayoutVars;
54
76
  setSegments(initialSegments?: number[]): void;
55
77
  setSegment(segmentIndex: number): void;
56
- spinOnce(stopAtSegments: number[], config?: Partial<ReelDealSpinConfig>): Promise<void>;
78
+ spinOnce(request: ReelDealSpinRequest | number[], configOverride?: Partial<ReelDealSpinConfig>): Promise<void>;
57
79
  private getSpinConfig;
58
80
  private getStopSpringOvershootPx;
59
- private getStopSpringOffset;
60
81
  spinQueue(spins: ReelDealSpinState[]): Promise<void>;
61
- stop(): void;
82
+ stop(error?: unknown): void;
62
83
  private resolvePendingSpins;
84
+ private rejectPendingSpins;
63
85
  requestResize(): void;
64
86
  private ensureReady;
65
- private attachButtonHandler;
66
87
  private handleButtonClick;
67
88
  private resolveButton;
68
- private resolveSpinQueue;
69
- private consumeNextSpin;
70
- private buildRandomSegments;
71
- private setButtonDisabled;
72
89
  private shouldAnimate;
73
90
  private startLoop;
74
- private loop;
91
+ private tick;
75
92
  private render;
76
- private getSpinBlurPxForReel;
77
- private renderWebGL;
78
93
  private updateVelocity;
79
94
  private updateIdle;
80
- private loadSprite;
81
95
  private setupResizeWatcher;
82
96
  private queueResize;
83
97
  private resizeToContainer;
84
98
  private applyViewportCrop;
85
99
  private applyResize;
86
100
  private getTargetDpr;
87
- private tryInitWebGL;
88
- private createWebGLProgram;
89
- private createWebGLShader;
90
- private releaseWebGLResources;
101
+ hideFinalLine(): void;
102
+ showFinalLineEffect(): void;
103
+ }
104
+
105
+ export declare interface ReelDealFinalSpinLine {
106
+ thickness: number;
107
+ intensity: number;
91
108
  }
92
109
 
93
110
  export declare interface ReelDealIdleBob {
@@ -98,18 +115,20 @@ export declare interface ReelDealIdleBob {
98
115
  }
99
116
 
100
117
  export declare interface ReelDealOptions {
101
- canvas: HTMLCanvasElement | string;
102
- container: HTMLElement | string;
103
- sprite: ReelDealSpriteSource;
104
- slotCount: number;
118
+ canvas?: HTMLCanvasElement | string;
119
+ container?: HTMLElement | string;
120
+ sprite?: ReelDealSpriteSource;
121
+ slotCount?: number;
105
122
  initialSegments?: number[];
106
123
  button?: HTMLButtonElement | string;
107
124
  queuedSpinStates?: ReelDealSpinState[];
108
125
  reels?: number;
109
126
  spinConfig?: Partial<ReelDealSpinConfig>;
110
127
  spinBlur?: Partial<ReelDealSpinBlur>;
128
+ webglShading?: Partial<ReelDealWebGLShading>;
111
129
  idleBob?: Partial<ReelDealIdleBob> | false;
112
130
  maxDpr?: number;
131
+ finalSpinLine?: Partial<ReelDealFinalSpinLine> | false;
113
132
  }
114
133
 
115
134
  export declare interface ReelDealSpinBlur {
@@ -119,11 +138,18 @@ export declare interface ReelDealSpinBlur {
119
138
 
120
139
  export declare interface ReelDealSpinConfig {
121
140
  durationMs?: number;
122
- minSpins: number;
141
+ minSpins?: number;
123
142
  speedPxS?: number;
124
143
  staggerMs?: number;
125
144
  }
126
145
 
146
+ export declare interface ReelDealSpinRequest {
147
+ stopAtSegments: number[];
148
+ config?: Partial<ReelDealSpinConfig>;
149
+ signal?: AbortSignal;
150
+ timeoutMs?: number;
151
+ }
152
+
127
153
  export declare interface ReelDealSpinState {
128
154
  stopAtSegments: number[];
129
155
  callback?: (spinIndex: number, stopAtSegments: number[]) => void;
@@ -131,4 +157,67 @@ export declare interface ReelDealSpinState {
131
157
 
132
158
  export declare type ReelDealSpriteSource = string | HTMLImageElement;
133
159
 
160
+ export declare interface ReelDealWebGLShading {
161
+ warpAngleDeg: number;
162
+ edgePower: number;
163
+ }
164
+
165
+ export declare interface Renderer {
166
+ init(init: RendererInit): void;
167
+ render(frame: RenderFrame): void;
168
+ onResize(width: number, height: number): void;
169
+ showFinalLine(): void;
170
+ hideFinalLine(): void;
171
+ dispose(): void;
172
+ getError(): string | null;
173
+ }
174
+
175
+ export declare type RendererInit = {
176
+ canvas: HTMLCanvasElement;
177
+ spriteImg: HTMLImageElement;
178
+ spriteWidth: number;
179
+ spriteHeight: number;
180
+ frameHeight: number;
181
+ reels: number;
182
+ visibleSlots: number;
183
+ heightScale: number;
184
+ spinBlur: ReelDealSpinBlur;
185
+ webglShading: ReelDealWebGLShading;
186
+ finalSpinLine: ReelDealFinalSpinLine | null;
187
+ };
188
+
189
+ export declare type RenderFrame = {
190
+ offsets: ArrayLike<number>;
191
+ velocities: ArrayLike<number>;
192
+ dpr: number;
193
+ };
194
+
195
+ export declare class WebGLRenderer implements Renderer {
196
+ private gl;
197
+ private glProgram;
198
+ private glBuffer;
199
+ private glTexture;
200
+ private glAttribs;
201
+ private glUniforms;
202
+ private config;
203
+ private canvas;
204
+ private spriteImg;
205
+ private webglInitError;
206
+ private lineRenderer;
207
+ private showLine;
208
+ init(init: RendererInit): void;
209
+ getError(): string | null;
210
+ render(frame: RenderFrame): void;
211
+ onResize(width: number, height: number): void;
212
+ showFinalLine(): void;
213
+ hideFinalLine(): void;
214
+ dispose(): void;
215
+ private tryInitWebGL;
216
+ private createWebGLProgram;
217
+ private createWebGLShader;
218
+ private getSpinBlurPxForReel;
219
+ private renderWebGL;
220
+ private renderFinalLine;
221
+ }
222
+
134
223
  export { }