Dreamy
A voxel creature sculpting studio for React: stamp and smear sculpts, paint them, rig them with premade skeletons and decals, and render them as normal 3D or pixel art. Meshing runs in Rust/WASM inside a Web Worker; the 3D view is Three.js. Creations save in the browser (IndexedDB) and can sync to a GitHub or GitLab repository.
Live demo: https://dreamy-poc-c89175.gitlab.io
Install
npm install @felbrasil/dreamy react react-dom three
react, react-dom (19) and three are peer dependencies. There is nothing else to configure: the Worker and
its WASM are inlined in the package, so Vite, Next.js (webpack or Turbopack) and other bundlers work as they are.
Use the whole studio
import { DreamyStudio } from "@felbrasil/dreamy";
import "@felbrasil/dreamy/styles.css";
export function App() {
return <DreamyStudio onReady={() => console.log("studio ready")} />;
}
The studio fills the page (fixed, full viewport), so render one per page. styles.css also loads the studio's
icons (Font Awesome) and font (Nunito) from their CDNs.
In Next.js you can render it straight from a server component: the package entry is a client module.
Import styles.css in your root layout.
Props
| Prop | Default | |
|---|---|---|
database | this browser's IndexedDB | Where the library of creations lives (LibraryDatabase: list / read / write / delete / current). |
storage | localStorage | Key-value storage for view preferences and git settings. |
gitSync | true | Offer Scenes → Git: sync the library with one GitHub or GitLab repository (token kept in storage). |
onReady | Called once the mesher is loaded, the creation is open and the studio has painted. |
Use the pieces
| Import | What |
|---|---|
@felbrasil/dreamy | DreamyStudio, the IndexedDB library, persistence, git sync, preferences |
@felbrasil/dreamy/core | createCreationApi: the event-sourced creation model, commands, undo, premades |
@felbrasil/dreamy/viewport | createViewport({ evaluator }): the Three.js renderer and interaction |
@felbrasil/dreamy/evaluator | createEvaluator(): the WASM mesher in a Worker (TypeScript fallback) |
@felbrasil/dreamy/api | Types: the model, commands, events and ports |
@felbrasil/dreamy/bake | createSpriteBaker(): pixel-art sprites of creations as palette roles, with no studio UI |
import { createCreationApi } from "@felbrasil/dreamy/core";
import { createEvaluator } from "@felbrasil/dreamy/evaluator";
import { createViewport } from "@felbrasil/dreamy/viewport";
const api = createCreationApi({ ids, clock, log });
const viewport = createViewport({ evaluator: createEvaluator() });
viewport.mount(hostElement, callbacks);
Bake sprites
import { createCreationApi } from "@felbrasil/dreamy/core";
import { createSpriteBaker, spriteColors } from "@felbrasil/dreamy/bake";
const api = createCreationApi({ ids, clock, log });
api.dispatch({ type: "GenerateCreature", payload: { seed: 42, chimera: false, variety: 0.7, target } });
const baker = createSpriteBaker();
const [front, back] = await baker.bake(api.creation(), [
{ view: { yaw: 15, pitch: 15 }, size: 256 }, // degrees around the creature's front
{ view: { yaw: 180, pitch: 30 }, size: 256 }, // its back, from above
{ view: { yaw: 15, pitch: 15 }, size: 64, dither: 0.2, normalStrength: 0.5 }, // less tone dither, flatter relief
]);
// front.roles: one palette role per pixel (0 transparent, 1 ink, 2–28 slot × tone); front.colors draws them,
// spriteColors(otherPalette) recolors them. front.anchor: the feet row and the silhouette's centre.
// front.bones: the skeleton (parent index, world position, sprite x / y, depth) for a bone or x-ray view.
Games that only draw baked sprites import the light @felbrasil/dreamy/sprite entry (no WebGL, mesher or React):
import { decodeSpritePng, encodeSpritePng, spriteColors, spriteRgba } from "@felbrasil/dreamy/sprite";
const png = await encodeSpritePng(front); // 8-bit indexed PNG: pixel = role, PNG palette = role colors
const { width, height, roles } = await decodeSpritePng(png);
const pixels = spriteRgba(roles, spriteColors(palette)); // ImageData pixels in any creature's colors
GenerateCreature takes a stage ("chibi", "young" or "adult"): the same seed is the same creature as a
big-headed baby, in between, or grown up.
The baker needs no React. It renders off-screen and also works in a hidden page or a headless browser with WebGL 2.
Requirements
A browser with WebGL 2, Web Workers and WebAssembly. Client-side only (no server rendering of the studio).
License
MIT