@drawcall/flipbook
Use @drawcall/flipbook for lightweight Three.js billboard effects loaded from KTX2 sprite sheets. Get ready-to-use flipbook assets from @drawcall/market.
A pre-rendered sprite sheet is the right choice for art-directed effects — explosions, flames, magic, impacts — where it looks far better than code-built particles and costs almost nothing at runtime. For procedural, parametric bursts instead (sparks thrown along a surface normal, physics debris, a count/direction that varies per event), use the pooled particles. The two compose: a flipbook explosion plus a procedural debris burst reads better than either alone.
Install
pnpm add @drawcall/flipbook three
three is a peer dependency.
Basic Use
import { Flipbook } from "@drawcall/flipbook";
import { KTX2Loader } from "three/addons/loaders/KTX2Loader.js";
const ktx2Loader = new KTX2Loader()
.setTranscoderPath("/basis/")
.detectSupport(renderer);
Flipbook.setKtx2Loader(ktx2Loader);
const flame = new Flipbook("/flame.ktx2", {
height: 0.6,
origin: "bottom-center",
billboard: "cylindrical"
});
scene.add(flame);
await flame.ready;
function animate(deltaSeconds: number) {
flame.update(deltaSeconds);
}
Use new Flipbook(url, options) when the asset URL is known up front. Use new Flipbook(options).load(url) or .setData(bytes) when loading is controlled elsewhere.
KTX2Loaderneeds the Basis transcoder files (basis_transcoder.js+.wasm) served at the path you pass tosetTranscoderPath. They are not bundled — copy them fromthree/examples/jsm/libs/basis/into your served static dir (e.g.public/basis/). Without them, Basis-compressed.ktx2files fail to decode and nothing renders — the most common "flipbook shows nothing" cause.
Get Assets
Prefer @drawcall/market for flipbook assets:
npx @drawcall/market search --type flipbook "flame"
Use the downloaded .ktx2 file as the Flipbook URL, for example new Flipbook("/effects/flame.ktx2").
KTX2 Requirements
The KTX2 may be Basis-compressed KTX2 loaded through Three.js KTX2Loader, or
uncompressed R8G8B8A8.
Required metadata:
flipbook.columnsflipbook.rows
Optional metadata:
flipbook.aspectRatio, defaults to the per-frame texture aspect ratioflipbook.frames, defaults tocolumns * rowsflipbook.duration, defaults toframes / 24flipbook.playbackRate, defaults to1flipbook.billboard, supportsspherical,cylindrical, andnone
Options
height: world-space plane height; width comes fromaspectRatioorigin: use"bottom-center"for flames, torches, impacts, and ground-anchored effectsbillboard: use"spherical"for free-facing sprites,"cylindrical"for upright Y-axis sprites, and"none"for manually oriented meshesplaybackRate: multiply the asset playback speedloop: setfalsefor one-shot effects like explosionsautoPlay: setfalsewhen triggering manuallyopacity: material opacity multiplierrandomStart: keeptruefor many looping effects so instances do not sync
Common Patterns
One-shot impact:
const impact = new Flipbook("/impact.ktx2", {
autoPlay: false,
loop: false,
origin: "bottom-center"
});
impact.restart();
Many looping particles:
const sparks = positions.map((position) => {
const spark = new Flipbook("/spark.ktx2", {
height: 0.25,
randomStart: true
});
spark.position.copy(position);
scene.add(spark);
return spark;
});
function animate(deltaSeconds: number) {
for (const spark of sparks) spark.update(deltaSeconds);
}
Debugging
- If nothing appears, await
flipbook.readyand check the URL response. - If frames are wrong, inspect
flipbook.columns,flipbook.rows, andflipbook.framesmetadata. - If the sprite faces oddly, switch between
spherical,cylindrical, andnone. - If the effect floats or sinks, adjust
originbefore adjusting mesh position. - If one-shot effects loop, set
loop: false.