mesh-avatar-studio
Turn one illustration into an animated 2D mesh avatar with a coding agent and a local editor
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Objects drawn in one bit, lit by your pointer. 25 ordered-dither 3D figures for the web, plus a film renderer in the same look.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Objects drawn in one bit, lit by your pointer.
Twenty-five 3D figures for the web: twenty objects and five whole scenes. Each sits on a plate; the cursor is a lamp hanging over it. Every dot is paper or ink, ordered by an 8×8 Bayer matrix, so the shading holds still while the light moves. Canvas, no runtime dependencies, 33 kB (15 kB gzipped) for all twenty-five.
There is also a film renderer in the same look (moving geometry, cameras and lights; dot-matrix type; a square-wave score) and agent skills, so Claude Code or Codex can make new figures and films and check their own frames.
Site: https://brk.bz/bitlight/
The frame at rest is the thumbnail, so each figure has a designed lamp position. Nothing in a figure moves except the light. Some read-outs say more than angles: the sundial tells the time, the moon names its phase, the dice name the brightest face.
Objects
Scenes: whole set-ups built from shared props. Same kernel, same budget.
objects Arch · Bowl · Cage · Columns · Dice · Gear · Keycap · Mac · Moon · Mug · Orb
Pins · Pocket watch · Rangefinder · Ring · Steps · Sundial · Tower · Vase · Ziggurat
scenes Workstation · Computer · Server room · Café · Cloud
npm i github:berkboz/bitlight # npm i bitlight once it is on npm
ES module
import { mount, sundial } from "bitlight";
const fig = mount(document.getElementById("figure"), sundial, {
cell: 3, // CSS px per dot (integer)
theme: "auto", // "light" (paper ground) | "dark" (night ground) | "auto"
onRead: (text) => (caption.textContent = text), // "shadow at 10:40"
});
// fig.lampAt(u, v) · fig.rest() · fig.retheme("dark") · fig.destroy()
React 18+
import { Bitlight } from "bitlight/react";
import { moon } from "bitlight";
<Bitlight figure={moon} cell={3} onRead={setCaption} />
Script tag
<div id="figure"></div>
<script src="https://cdn.jsdelivr.net/gh/berkboz/bitlight/dist/bitlight.global.js"></script>
<script>Bitlight.mount(document.getElementById("figure"), "orb", { cell: 3 });</script>
shadcn: vendors the source into lib/bitlight:
npx shadcn@latest add https://berkboz.github.io/bitlight/registry/bitlight.json
Four knobs, all optional, all live (handle.set()), none of them re-march the
geometry. Full reference, ranges and the roadmap: docs/CONTROLS.md.
mount(el, sundial, {
ink: { lit: "#f04820", unlit: "#0f0f0f" }, // any two CSS colours
screen: "dots", // bayer · bayer4 · dots · lines · diagonal · noise
tones: 4, // 2 (default, one bit) to 16 inks; or palette: ["#000", ...]
light: { power: 3.2, falloff: 0.32, ambient: 0.035, contrast: 0.72, spot: true },
});
fig.set({ screen: "lines" }); // partial updates; fig.options returns what is live
ink, or two CSS custom properties on the host or any ancestor
(--bitlight-lit, --bitlight-unlit), which recolour every figure below
them. theme: "light" paints empty ground with lit, "dark" with unlit.
Keep 3:1 contrast or the edges sink.2 is one bit. 4, 8, 16 step through more inks between unlit and lit, or give an exact palette.spot swaps the bare lamp for a cone.Try them live in the Lab on the site; it writes the matching snippet and the page URL is a shareable link to the look.
requestAnimationFrame for the whole page; off-screen or settled figures
do no work.prefers-reduced-motion places the lamp directly.role="img" and an aria-label from its means.For moving geometry, many lights, every frame. bitlight/gpu runs the same
kernel (src/core.js: march, soft shadow, shade, halo, screens) as a WebGL2
shader; the scene is GLSL with the same sd helpers. Optional: gpu() returns
null without WebGL2 and float render targets, and nothing else needs it.
import { gpu } from "bitlight/gpu";
const view = gpu(canvas, { glsl: `
float map(vec3 p) { return min(p.y, sdSphere(p - vec3(0, .62 + .1 * sin(uTime), 0), .62)); }
float occ(vec3 p) { return sdSphere(p - vec3(0, .62 + .1 * sin(uTime), 0), .62); } // casters only
void material(vec3 p, vec3 n, inout Mat m) { m.a = 0.9; }` });
view.render({ lights: [{ p: [-0.9, 1.6, 1.2] }], tones: 4, time: performance.now() / 1000 });
The same screens work on photographs, video and a camera feed. Nothing is uploaded: it runs in your browser or with FFmpeg on your machine.
photo.html, live at https://brk.bz/bitlight/photo/.
Drop an image or a video, or switch on the camera. Pick ink and screen, tune
contrast, mid-tones and sharpness, then save a PNG or record the result as video.node bitlight.mjs image portrait.jpg --screen dots --ink f04820,0f0f0f
node bitlight.mjs video clip.mp4 --tones 4 --palette 0f380f,306230,8bac0f,9bbc0f --cols 240
Options: --cols dots across, --cell pixels per dot, --screen, --tones,
--ink LIT,UNLIT or --palette A,B,..., --contrast --brightness --gamma --sharpen --invert --no-auto, and for video --fps, --smooth (temporal
steadiness) and --no-audio. Audio is kept.
bitlight/image (process, or the steps: lumaFromRGBA,
toGrid, tune, dither, paint). No DOM, no decoding, so it runs anywhere.Tips: more dots (--cols 320+) gives faces enough detail at 1 bit; 4 tones holds
a face with far fewer; --sharpen is what keeps thin edges alive. Because every
screen is fixed, dither does not crawl between video frames.
A figure is one file with one define() call:
import { define, sd } from "bitlight";
export default define({
name: "Orb",
means: "A ball resting on a plate. Move the lamp and its shadow swings round.",
sdf: (x, y, z) => sd.sphere(x, y - 0.62, z, 0.62), // plate top is y = 0
bound: [0, 0.62, 0, 0.64], // sphere enclosing it
rest: [-0.25, 1.5, 1.35], // lamp at rest = the thumbnail
});
Scenes compose the shared props in src/props.js (a computer with fans, a desk
set, server racks, a café table with a laptop, a cloud). See
src/figures/workstation.js.
The full workflow (pitch, build, check, look) is
skills/bitlight-create/SKILL.md, written for
an AI agent and readable by people. In short:
node build.mjs # regenerate index, bundle, registry
node look.mjs mine # eight lamps, rule checks → sheets/mine-light.png
node look.mjs mine --theme dark
Then open the sheets. Passing is necessary, not sufficient.
| # | Rule | |
|---|---|---|
| 01 | Two inks | Paper and ink, any two colours. No grey, no alpha, no anti-aliasing. |
| 02 | One light | The pointer is the only input. Geometry never moves in a figure. |
| 03 | Reach | The lamp is clamped to the plate; at the far edge the figure is still composed. |
| 04 | Order | Ordered screens only (Bayer by default), never error diffusion: diffused dots crawl when the light moves. |
| 05 | Edge | A one-dot paper gap separates a near silhouette from what is behind it. |
| 06 | Range | Every lamp position leaves both inks on the plate. |
| 07 | Rest | The rest frame is the thumbnail. Light the faces the camera sees. |
| 08 | Clock | A stiff spring follows; a soft one carries the lamp home. |
| 09 | Cost | One frame loop; settled figures sleep; re-light < 16 ms. |
| 10 | Quiet | No text inside a figure. Words live in the read-out. |
Details and how each is enforced: skills/bitlight-create/rules.md.
Same lighting code as the figures (src/core.js), but geometry, cameras and
lights may move every frame. Words are drawn on the same dot grid; shots cut
hard; the score is square waves. The square ones are made for feeds. Needs FFmpeg.
| Move the light | One light | A day, fast | Twenty-five |
Previews are silent GIFs; the links open the full films with sound
(mp4 and webm in media/). The 16:9 one, scenes,
is 35 s of the props in motion.
| Film | Format | What it shows |
|---|---|---|
pointer | 1:1 · 13 s | a cursor drives the lamp across four figures |
moon-loop | 1:1 · 8 s | one orbit of the lamp, every phase, loops seamlessly |
sundial-day | 1:1 · 11 s | dawn, a day, dusk; the read-out is the clock |
montage | 1:1 · 16 s | all 25 figures on the beat |
launch | 16:9 · 42 s · 4K | the launch film: a dead image, a cursor that lights it, a pocket watch, a Mac, the library on the beat, a build-up, the install line |
launch-spin | 16:9 · 27 s · 4K | the objects themselves turn: turntable, racing hands, an orbiting camera, twelve figures spinning on the beat |
launch-teaser | 16:9 · 15 s · 4K | the first beats of launch, for the day before |
launch-loop | 1:1 · 13 s | rangefinder, pocket watch and Mac lit by a cursor, for feeds |
scenes | 16:9 · 35 s | the props in motion: fans, a lift, a walking lamp, a lid opening |
node film/check.mjs --film pointer # rules + contact sheet
node film/render.mjs --film pointer --stills 2,8
node film/render.mjs --film pointer && node film/score.mjs --film pointer && node film/mux.mjs --film pointer
A film is one file: shots that are pure functions of time, plus a score.
Films of figures are a few lines each (film/films/pointer.mjs); --film
also takes a path, so films can live in another project. Workflow and lessons:
skills/bitlight-film/SKILL.md.
Fish by Orkan Celikhisar. A fish smoking a cigarette in an endless deep sea. It follows your pointer; hold to take a drag, let go to exhale. Orkan moved the kernel (the lamp model and the Bayer screen) into a WebGL2 fragment shader, so the look runs on the GPU instead of the CPU: many lights, moving geometry, every frame. 14 s clip.
Built something on it? Open a PR that adds it here.
src/core.js the look: lamp model, tone, shadow, screens, halo (no DOM)
src/bitlight.js define() + mount(): cache, input, motion, a11y
src/gpu.js the same look as a WebGL2 shader, for scenes written in GLSL (optional)
src/props.js shared geometry for scenes and films
src/react.js <Bitlight />
src/figures/ one file per figure (index.js is generated)
film/ offline renderer: engine, figure, type, frame, render, score, mux, check
film/films/ the films above
docs/CONTROLS.md ink, screen, lamp: reference and plan
skills/ agent skills: bitlight-create, bitlight-film
media/ rendered films for the site; made-with/ holds clips of other people's work
look.mjs figure checker + frame sheets
test/smoke.mjs package test (import, bounds, types, bundle, React, registry)
build.mjs generated files; --check for CI
index.html the site: inspector, Lab, gallery, films
photo.html photo, video and camera tool (runs in the browser)
bitlight.mjs the same as a command: image and video through FFmpeg
src/image.js the picture pipeline (no DOM)
Node 20+, Chromium via Playwright, FFmpeg for films.
npm install
npm run serve # http://127.0.0.1:5173 (ES modules need http, not file://)
npm run check # everything CI runs
The structure (a fixed kernel, written rules, and an agent skill that checks its own frames) follows Hairline by Lucas Markes. The look, kernel, figures and films are original.
MIT.