agentsclimarketplace

Textmode js

Skill vinsonconsulting/claude-skill-foundry/skills/ascii-art/textmode-js

A scoring harness for Claude Agent Skills. Each carded skill ships a Skill Card: a SkillSpector security scan and trigger evals, generated and gated in CI.

Install
npx -y skills add vinsonconsulting/claude-skill-foundry --skill textmode-js

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing 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.

What its author says it does

Copied from the file, not written here

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.

SKILL.md

6.5 KB, 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 — a rect/point with 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 until pop() 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

  1. Pick a target and entry point. Browser ESM (bundler/importmap) or a single UMD HTML file. See references/setup.md.
  2. Stand up the lifecyclecreatesetup (load assets) → draw. Wire windowResized to resizeCanvas. Confirm a blank background() renders before adding logic.
  3. Draw. Set char + charColor + cellColor, then primitives or print. Remember: cells, center origin, sticky state, push/pop to isolate. See references/drawing.md and references/text-and-ramps.md.
  4. Animate / generate. Use frameCount, noise, seeded random, map/lerp. See references/animation-and-generative.md.
  5. Composite only when one pass isn't enough — layers, filters, custom shaders. See references/compositing.md.
  6. Export to text, vector, image, or video — t.saveStrings/t.saveSVG/ t.saveCanvas/t.saveGIF/t.saveVideo. See references/media-and-export.md.

Which reference to open

You're working on…Open
Install, create() options, canvas/WebGL2, lifecycle, loop controlreferences/setup.md
Grid & coordinates, primitives, char/charColor/cellColor, transformsreferences/drawing.md
print, alignment, inline markup, TextmodeGlyphRamp value→glyphreferences/text-and-ramps.md
Timing, seeded random & streams, noise, vectors, easing, math helpersreferences/animation-and-generative.md
Layers & blend modes, framebuffers, filters, GLSL shaders, 3D camerareferences/compositing.md
Loading images/video, media→ASCII conversion, exporting TXT/SVG/PNG/GIF/MP4references/media-and-export.md

Debugging checklist

  • Nothing renders / black canvas. Confirm WebGL2 (a very old browser fails). Confirm draw() actually runs and background() 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 charColor set once persists. Wrap isolated changes in push()/pop().
  • Assets are blank. loadImage/loadVideo/loadFont/createMaterialShader are async — await them in setup(), and call video.play() for video.
  • Resize looks wrong. Only resizeCanvas() in windowResized updates the grid; read t.grid.cols/rows after 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.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.