Install
pnpm add @nachi-vfx/core @nachi-vfx/three three@0.185.1
pnpm add -D @types/three@0.185.0 # TypeScript
TSL · WebGPU · Three.js
Composable particle modules, timelines, fluid grids, neighbor simulation, and debugging — without an editor runtime.
Getting started
pnpm add @nachi-vfx/core @nachi-vfx/three three@0.185.1
pnpm add -D @types/three@0.185.0 # TypeScript
import {
VFXSystem, billboard, burst, defineEffect,
defineEmitter, drag, gravity, lifetime,
positionSphere,
} from '@nachi-vfx/core'
import {
createThreeKernelAdapter,
createThreeRuntimeRenderer,
materializeThreeSpriteDraw,
} from '@nachi-vfx/three'
import * as THREE from 'three/webgpu'
const sparks = defineEmitter({
capacity: 512,
spawn: burst({ count: 120 }),
init: [positionSphere({ radius: .2 }), lifetime(.8)],
update: [gravity(-9.8), drag(.35)],
render: billboard({ blending: 'additive' }),
})
const effect = defineEffect({ elements: { sparks } })
const renderer = new THREE.WebGPURenderer()
renderer.setSize(innerWidth, innerHeight)
document.body.append(renderer.domElement)
await renderer.init()
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(
50, innerWidth / innerHeight, .1, 100,
)
camera.position.set(0, 1, 5)
const adapter = createThreeKernelAdapter({ backend: 'webgpu' })
const runtimeRenderer = createThreeRuntimeRenderer(renderer, adapter)
const system = new VFXSystem(runtimeRenderer, scene)
const instance = system.spawn(effect, { position: [0, 1, 0], seed: 42 })
const emitter = instance.getEmitter('sparks')
if (!emitter) throw new Error('Missing sparks emitter')
const draw = materializeThreeSpriteDraw(emitter.program, emitter.kernels)
scene.add(draw)
let previous = performance.now()
async function frame(now) {
await system.update(Math.min((now - previous) / 1000, .1))
previous = now
renderer.render(scene, camera)
requestAnimationFrame(frame)
}
requestAnimationFrame(frame)
The repository Pages deployment serves the playground and showcase below this documentation root. A standalone docs deployment must configure or omit those demo links; the npm Quick Start above has no playground dependency.
Core guides
Choose capacity and deterministic burst, rate, or distance spawning. Lifecycle, prewarm, looping, bounds, and quality stay declarative.
Compose Init and Update behavior in author order. Access manifests make dynamic attributes and custom TSL scratch modules inspectable.
Sequence play, stop, markers, camera shake, and hit stop on an effect-local clock with deterministic loop boundaries.
Compose scrolling maps, independent dissolve UVs, runtime opacity, and edge emission
with fxMaterial. Put hold thresholds below the noise texture's minimum
value, and tune noise frequency to the mesh scale.
Use four quality tiers, distance/frustum culling, significance scores, particle budgets, and bounded resource pools.
Bake renderer-read attributes to Float32 or quantized binary snapshots and replay without scheduling simulation kernels.
Capture logical particle rows and frame-local profiles through the serialized system queue; unavailable GPU timing remains explicit.
Run packed Grid2D/3D stages for injection, advection, buoyancy, Jacobi pressure, projection, and particle transfer.
Build a bounded GPU neighbor grid for flocking, custom neighbor TSL, and iterative Jacobi PBD distance constraints.
Golden gallery
API reference
The normative RFC documents stage semantics, runtime ownership, diagnostics, serialization, and package boundaries. Package READMEs provide focused entry points.