blue-archive-touch-effect
v2.0.0
Published
Unity-faithful Blue Archive touch effects for the web.
Readme
blue-archive-touch-effect
Attachable browser runtime from the Blue Archive Touch Effect.
Install
Version 2 is ESM-only and targets modern ES2022 browsers with WebGL (current Chromium, Firefox, and WebKit).
npm install blue-archive-touch-effectWhat It Does
- Attaches to a dedicated overlay container
- Adds a transparent overlay canvas with
pointer-events: none - Uses the host element as a fixed CSS
screencompositing boundary - Reproduces the verified Unity Rebuilt click particles, swipe particles, TrailRenderer geometry, custom shaders, and MX Final Bloom
- Handles concurrent mouse, touch, and pen gestures without a global gesture limit
- Exposes a small runtime API for manual triggering, config updates, resize, and cleanup
Minimal Usage
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 })Configuration
Pass a nested public config patch at creation time and update it later by section.
import { createTouchEffect } from 'blue-archive-touch-effect'
const fx = createTouchEffect({
target,
config: {
performance: {
maxFps: 'display',
pixelRatioCap: 2,
},
appearance: {
tint: { r: 1, g: 1, b: 1 },
intensity: 1,
opacity: 1,
},
click: {
enabled: true,
scale: 1.1,
playbackRate: 1,
layers: {
dissolveRing: { enabled: true, tint: { r: 1, g: 1, b: 1 }, intensity: 1, opacity: 1 },
core: { enabled: true, tint: { r: 1, g: 1, b: 1 }, intensity: 1, opacity: 1 },
particles: { enabled: true, tint: { r: 1, g: 1, b: 1 }, intensity: 1, opacity: 1 },
},
},
swipe: {
particles: {
enabled: true,
tint: { r: 1, g: 1, b: 1 },
intensity: 1,
opacity: 1,
scale: 1,
speed: 1,
lifetime: 1,
density: 5,
},
trail: {
enabled: true,
tint: { r: 1, g: 1, b: 1 },
intensity: 1,
opacity: 1,
duration: 0.3,
width: 0.005,
},
},
bloom: {
enabled: true,
intensity: 1.7,
threshold: 1,
},
input: {
pointerCapture: true,
slideThreshold: 0.005,
},
},
})
fx.updateConfig({
appearance: { opacity: 0.9 },
click: { scale: 1.2 },
swipe: { trail: { duration: 0.4 } },
})updateConfig(...) deep-merges known public fields. RGB and opacity are clamped to [0, 1]; nonnegative multipliers and dimensions are clamped at zero; click.playbackRate stays positive; numeric FPS values become integers of at least 1.
Bloom Reference
The renderer uses the Unity Rebuilt Copy → Prefilter → Downsample → Upsample → Final pipeline. Public configuration is deliberately limited to:
bloom.enabledbloom.intensity, default1.7bloom.threshold, default1
The verified diffusion, soft knee, clamp, anamorphic ratio, tint, shader formulas, and pyramid behavior stay internal. The scene target prefers HDR and silently falls back to RGBA8 when the browser cannot allocate it. Bloom is applied before the overall appearance tint, intensity, and opacity.
API
createTouchEffect({ target, listenTarget, autoBindPointer, config })trigger(point)begin(pointerId, point)move(pointerId, point)end(pointerId)endAll()updateConfig(partial)resize()dispose()
trigger(point) plays only the click effect. begin, move, and end manage a complete click-and-drag gesture. Points have the shape { x, y, space: 'client' | 'local' }; client means viewport coordinates and local means CSS pixels relative to the target's top-left corner. Out-of-bounds trigger, begin, and move calls return false.
Options
target: required host element for the overlay canvaslistenTarget: optional element or window used for pointer listening; defaults totargetautoBindPointer: adds mouse, touch, and pen listeners; defaults totrueconfig: optional deep patch of the public TouchEffect config
The DPR cap is configured through performance.pixelRatioCap. Frame rate uses performance.maxFps, either 'display' or a numeric limit. Limiting FPS skips draws while simulation continues to use real elapsed time. The render loop sleeps when no visible effect remains. Recoverable WebGL context loss freezes existing visuals, rebuilds GPU resources on restore, and then accepts input again.
Manual Triggering
Set autoBindPointer: false when your application owns the input lifecycle.
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()
})Call trigger(point) instead when you want a click effect without starting a swipe gesture.
Lab
The repository Lab demonstrates automatic input and every public configuration field. Its controls are not part of the package API and do not expose Unity internals.
License
MIT. See LICENSE.
