Textmode js
Skill vinsonconsulting/claude-skill-foundry/skills/ascii-art/textmode-js
Use when building real-time ASCII or textmode graphics in the browser with the textmode.js library — generative glyph-grid sketches, retro/terminal visuals, audio-reactive or VJ textmode, or turning images and video into ASCII on a WebGL2 character grid. Trigger whenever textmode.js is named, when a sketch calls textmode.create, t.setup/t.draw, t.grid, t.char/charColor/cellColor, glyph ramps, or character-cell rendering, or when the user wants to build, debug, or export a grid-of-characters visual. Covers UMD/ESM setup, the setup/draw/resize lifecycle, drawing primitives, char and color cells, print and glyph ramps, animation and noise, layers, filters, custom GLSL ES 3.00 shaders, media conversion, and export to TXT/SVG/PNG/GIF/MP4/WebM. Not for generic ASCII art or Python image-to-ASCII — this is specifically the textmode.js JavaScript/TypeScript library.From its SKILL.md
npx -y skills add vinsonconsulting/claude-skill-foundry --skill textmode-jsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
- fetches URLsInstructs the agent to fetch 1 URL, including https://cdn.jsdelivr.net/npm/textmode.js@latest/dist/textmode.umd.js.
SKILL.md
6.5 KB, ~1.6k tokens by cl100k_base, as published. Nobody here has run it
textmode-js
textmode.js is a zero-dependency TypeScript creative-coding library for real-time
ASCII / textmode graphics. It renders to a grid of character cells on a WebGL2
canvas with p5.js-like ergonomics. Use it to author, debug, and export
glyph-grid sketches.
Mental model
- The canvas is a grid of cells, not pixels. Each cell holds one glyph, a char color (the glyph's foreground), and a cell color (its background).
- You draw with p5-style primitives, but coordinates and sizes are in cells.
- The origin
(0,0)is the grid CENTER. +x is right, +y is down, -y is up. This is the single biggest trip-up — arect/pointwith no transform lands at the center, not the top-left. - A sketch is a lifecycle:
create()once →setup()once →draw()every frame. State setters (char,charColor,cellColor, transforms) are sticky — they apply to every draw call until changed or untilpop()restores them.
Minimal sketch (ESM)
import { textmode } from "textmode.js";
const t = textmode.create({
width: window.innerWidth,
height: window.innerHeight,
fontSize: 16,
frameRate: 60,
});
t.setup(() => {
// Runs once after the renderer + grid exist.
// Load fonts / images / shaders here (these are async — await them).
});
t.draw(() => {
t.background(18, 20, 28); // clear the grid every frame
t.char("@"); // glyph used by following draw calls
t.charColor(120, 220, 160); // glyph (foreground) color
t.cellColor(0, 0, 0); // cell (background) color
// (0,0) is the grid CENTER; rect(w,h) is centered there, sized in CELLS.
t.rect(t.grid.cols / 2, t.grid.rows / 2);
});
t.windowResized(() => t.resizeCanvas(window.innerWidth, window.innerHeight));
UMD is identical minus the import — load the script, then use the global
textmode:
<script src="https://cdn.jsdelivr.net/npm/textmode.js@latest/dist/textmode.umd.js"></script>
<script>
const t = textmode.create({ width: 800, height: 600, fontSize: 16, frameRate: 60 });
t.draw(() => { t.background(0); t.char("#"); t.charColor(0, 255, 0); t.rect(10, 6); });
</script>
Generative idiom (per-cell noise + glyph ramp)
A glyph ramp maps a value in [0,1] to a character (dark → light). Stamp one cell
at a time by translating to it and drawing a point():
let ramp;
t.setup(() => { ramp = t.createGlyphRamp(" .:-=+*#%@"); });
t.draw(() => {
t.background(0);
const t0 = t.frameCount * 0.01;
for (let y = 0; y < t.grid.rows; y++) {
for (let x = 0; x < t.grid.cols; x++) {
const n = t.noise(x * 0.08, y * 0.12, t0); // 0..1
t.push();
t.translate(x - t.grid.cols / 2, y - t.grid.rows / 2); // cell -> centered coords
t.char(ramp.at(n));
t.charColor(60 + n * 180, 120, 220 - n * 140);
t.point();
t.pop();
}
}
});
Authoring workflow
- Pick a target and entry point. Browser ESM (bundler/importmap) or a single
UMD HTML file. See
references/setup.md. - Stand up the lifecycle —
create→setup(load assets) →draw. WirewindowResizedtoresizeCanvas. Confirm a blankbackground()renders before adding logic. - Draw. Set
char+charColor+cellColor, then primitives orprint. Remember: cells, center origin, sticky state,push/popto isolate. Seereferences/drawing.mdandreferences/text-and-ramps.md. - Animate / generate. Use
frameCount,noise, seededrandom,map/lerp. Seereferences/animation-and-generative.md. - Composite only when one pass isn't enough — layers, filters, custom shaders.
See
references/compositing.md. - Export to text, vector, image, or video —
t.saveStrings/t.saveSVG/t.saveCanvas/t.saveGIF/t.saveVideo. Seereferences/media-and-export.md.
Which reference to open
| You're working on… | Open |
|---|---|
Install, create() options, canvas/WebGL2, lifecycle, loop control | references/setup.md |
Grid & coordinates, primitives, char/charColor/cellColor, transforms | references/drawing.md |
print, alignment, inline markup, TextmodeGlyphRamp value→glyph | references/text-and-ramps.md |
| Timing, seeded random & streams, noise, vectors, easing, math helpers | references/animation-and-generative.md |
| Layers & blend modes, framebuffers, filters, GLSL shaders, 3D camera | references/compositing.md |
| Loading images/video, media→ASCII conversion, exporting TXT/SVG/PNG/GIF/MP4 | references/media-and-export.md |
Debugging checklist
- Nothing renders / black canvas. Confirm WebGL2 (a very old browser fails).
Confirm
draw()actually runs andbackground()is called each frame. Check the console for a renderer-init error. - Drawing is off-screen or "in the corner." You assumed top-left origin. The
center is
(0,0); offset cell loops by-cols/2, -rows/2(see the idiom above). - Everything is one color / wrong glyph. State is sticky and global to the
active layer — a
charColorset once persists. Wrap isolated changes inpush()/pop(). - Assets are blank.
loadImage/loadVideo/loadFont/createMaterialShaderare async —awaitthem insetup(), and callvideo.play()for video. - Resize looks wrong. Only
resizeCanvas()inwindowResizedupdates the grid; readt.grid.cols/rowsafter resize to get the new dimensions.
Conventions
- Keep media and font files local to the project; reference paths, not data URLs.
- No secrets in a sketch. If a sketch hits an API for live data, name where the key lives (env/secret store) and read it at runtime — never inline it.
What ships with it: 8 files
25.2 KB alongside SKILL.md
evals/
- evals.json3.4 KB
references/
- animation-and-generative.md3.1 KB
- compositing.md3.4 KB
- drawing.md3.4 KB
- media-and-export.md3.4 KB
- setup.md3.4 KB
- text-and-ramps.md2.3 KB
- README.md2.7 KB