textmode-js — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited textmode-js (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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.
char color (the glyph's foreground), and a cell color (its background).
This is the single biggest trip-up — a rect/point with no transform lands at the center, not the top-left.
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.
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>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();
}
}
});UMD HTML file. See references/setup.md.
create → setup (load assets) → draw. WirewindowResized to resizeCanvas. Confirm a blank background() renders before adding logic.
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.
frameCount, noise, seeded random, map/lerp.See references/animation-and-generative.md.
See references/compositing.md.
t.saveStrings/t.saveSVG/t.saveCanvas/t.saveGIF/t.saveVideo. See references/media-and-export.md.
| 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 |
Confirm draw() actually runs and background() is called each frame. Check the console for a renderer-init error.
center is (0,0); offset cell loops by -cols/2, -rows/2 (see the idiom above).
active layer — a charColor set once persists. Wrap isolated changes in push()/pop().
loadImage/loadVideo/loadFont/createMaterialShaderare async — await them in setup(), and call video.play() for video.
resizeCanvas() in windowResized updates thegrid; read t.grid.cols/rows after resize to get the new dimensions.
lives (env/secret store) and read it at runtime — never inline it.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.