Blue Archive Cursor Touch Effect
September 4, 2026 · View on GitHub
Quick Start
Install dependencies and start the test page:
npm install
npm run dev
Build the project:
npm run build
Use The NPM Package
Version 2 is ESM-only and targets ES2022-capable browsers with WebGL. It is intended for current Chromium, Firefox, and WebKit releases.
npm install blue-archive-touch-effect
import { createTouchEffect } from 'blue-archive-touch-effect'
const target = document.querySelector<HTMLElement>('#fx-root')
if (!target) {
throw new Error('Missing target element')
}
const fx = createTouchEffect({ target })
The runtime appends a transparent overlay canvas to target and uses target as the fixed CSS screen compositing boundary. Use a dedicated, otherwise empty overlay element for target. If it is statically positioned, the runtime temporarily sets it to position: relative; its original inline positioning and blend styles are restored on dispose().
Runtime API
const fx = createTouchEffect({
target,
listenTarget,
autoBindPointer,
config,
})
fx.trigger({ x: 120, y: 80, space: 'local' })
fx.begin(pointerId, { x: clientX, y: clientY, space: 'client' })
fx.move(pointerId, { x: clientX, y: clientY, space: 'client' })
fx.end(pointerId)
fx.endAll()
fx.updateConfig(partialConfig)
fx.resize()
fx.dispose()
Options
target: required host element for the overlay canvas.listenTarget: optional element orwindowused for pointer input; defaults totarget.autoBindPointer: automatically handles mouse, touch, and pen input; defaults totrue.config: modifies the touch-effect configuration.
Manual Triggering
By default, the runtime owns pointer input. Set autoBindPointer: false if your app wants to manage click and drag gestures itself.
const fx = createTouchEffect({
target,
autoBindPointer: false,
})
const point = (event: PointerEvent) => ({
x: event.clientX,
y: event.clientY,
space: 'client' as const,
})
target.addEventListener('pointerdown', (event) => {
fx.begin(event.pointerId, point(event))
})
target.addEventListener('pointermove', (event) => {
fx.move(event.pointerId, point(event))
})
target.addEventListener('pointerup', (event) => {
fx.end(event.pointerId)
})
target.addEventListener('pointercancel', (event) => {
fx.end(event.pointerId)
})
window.addEventListener('blur', () => {
fx.endAll()
})
local coordinates are CSS pixels relative to the target's top-left corner. client coordinates are viewport coordinates. Out-of-bounds trigger, begin, and move calls return false.
Configuration Example
fx.updateConfig({
performance: {
maxFps: 60,
pixelRatioCap: 2,
},
appearance: {
tint: { r: 0x4C / 255, g: 0xA7 / 255, b: 1 },
intensity: 1,
opacity: 1,
},
click: {
scale: 1.15,
playbackRate: 1,
layers: {
particles: {
tint: { r: 1, g: 1, b: 1 },
intensity: 1.2,
},
},
},
swipe: {
particles: {
density: 7,
speed: 1.1,
lifetime: 0.9,
},
trail: {
duration: 0.4,
width: 0.006,
},
},
bloom: {
enabled: true,
threshold: 1,
intensity: 1.7,
},
})
updateConfig(...) deep-merges by section, so callers only need to pass the fields they want to change. RGB and opacity are clamped to [0, 1]; sizes, speeds, lifetimes, densities, widths, and intensities are nonnegative.
References
- Effect implementation reference: the Unity scene and its referenced components, shaders, materials, meshes, and textures.
License
This project uses the MIT License. See LICENSE.