@eluvade/rift-echoes
v1.1.0
Published
Lightweight WebGL module for rendering interactive loot drops with rarity-based visuals.
Maintainers
Readme
Rift Echoes renders animated, rarity-tiered loot-drop effects ("cargo caches") in the browser. It's a self-contained WebGL2 + TypeScript module with zero runtime dependencies — no three.js, no PixiJS. Each drop is composed from additively-blended emitter layers rendered into an HDR framebuffer and finished with bloom and ACES tonemapping.
Built for Abyssal Rift — a 2D space exploration MMORPG.
▶ Live demo · Layer × rarity grid · npm
Features
- Six rarity tiers — Common, Uncommon, Rare, Epic, Legendary, Unique — each with its own colour, star shape, and particle density.
- Pure WebGL2 instanced rendering; emitters of the same kind batch into a single draw call across every live drop.
- HDR pipeline (
RGBA16F→ bloom → ACES) so emissive cores can bloom past 1.0. - Many drops share one canvas and one renderer; place each by
x/y.
Install
npm install @eluvade/rift-echoesWithout a build step (CDN)
The package is a native ES module with zero dependencies, so you can import it
straight from a CDN inside a plain <script type="module"> — no bundler, no
package manager, just an HTML file:
<script type="module">
import { RiftRenderer, Rarity } from 'https://esm.sh/@eluvade/[email protected]';
// Omit `canvas` and a full-screen one is created and appended for you.
// All textures are generated procedurally — nothing is fetched.
const renderer = new RiftRenderer();
await renderer.ready();
const dpr = window.devicePixelRatio || 1;
renderer.createCargoCache({
rarity: Rarity.Unique,
x: (innerWidth / 2) * dpr, // createCargoCache uses device pixels
y: (innerHeight / 2) * dpr,
size: 1.5,
});
</script>esm.sh is used above; jsDelivr
(https://cdn.jsdelivr.net/npm/@eluvade/[email protected]/+esm) and
unpkg (https://unpkg.com/@eluvade/[email protected]) serve
the same module. Pin @1.0.0 for reproducibility, or drop it to track latest.
Usage
import { RiftRenderer, Rarity } from '@eluvade/rift-echoes';
// Pass your own canvas, or omit it to have one appended full-screen.
const renderer = new RiftRenderer();
// Renderer init is async (GL setup); wait before spawning.
await renderer.ready();
const drop = renderer.createCargoCache({
rarity: Rarity.Legendary,
x: 400, // device pixels
y: 300,
size: 1.0,
});
// When the drop is collected / despawned:
drop.destroy();API
new RiftRenderer(options)
new RiftRenderer() works with no arguments; the only option is optional.
| option | type | description |
| ------------- | ------------------- | ------------------------------------------------------------------ |
| canvas | HTMLCanvasElement | Optional. If omitted, a full-screen canvas is created and appended. |
renderer.ready(): Promise<void>— resolves once textures are loaded and the render loop is running.renderer.createCargoCache(params): CargoCache— spawns a drop.params:{ rarity, x?, y?, size? }.renderer.destroy()— tears down GL resources and stops the loop.
CargoCache
cache.x,cache.y— mutable; move a live drop by assigning.cache.destroy()— plays the despawn and removes the drop.cache.finished—trueonce the despawn has fully played out.
Rarity / RARITY_CONFIGS
Rarity is an enum (Common…Unique). RARITY_CONFIGS exposes the layer
recipe for each tier (colour + ordered emitter layers) if you want to inspect or
fork the visual definitions.
Examples
Two live harnesses live under examples/ and are deployed to GitHub Pages:
- Single-drop tuning harness
—
examples/index.html. Spawn one drop and tune it live: pick the rarity, toggle individual layers, override a layer's DMP, and respawn / destroy. URL params:?rarity=<Rarity>,?layers=<i,j>,?dmp=<a,b,c,d>,?freeze=<radians>. Exposeswindow.riftRenderer/window.riftCache. - Layer × rarity grid
—
examples/grid.html. Every layer isolated against every rarity tier in one canvas, with per-row sliders to tune each layer.
To run them locally: npm run build, then serve the repo root with any static
server (e.g. npx serve .) and open examples/index.html or
examples/grid.html.
Development
npm run build # tsc → dist/
node scripts/shoot.mjs <label> <Rarity> [layers] # single headless screenshot
node scripts/contact.mjs <label> # per-layer × per-rarity contact sheet (PNG)