# ASCII Portrait — `ascii-portrait@1.0.0`

A sculpted head made of thousands of glowing text characters decodes out of scrambled symbols, slowly turns, and types a caption underneath.

The ASCII portrait look from this week's feeds: a picture redrawn with monospace characters, faint dots in the shadows and dense symbols in the light, like a terminal screen or a typewriter print. The demo is a classical marble bust that unscrambles out of random symbols, turns slowly under a moving light and then types a line of text with a blinking cursor. Change the characters (classic, code, binary or blocks), their size, the colours (green screen, amber, ink on paper), the glow and the caption. The renderer also works on anything already drawn on a canvas, so you can turn your own footage or 3D scene into text.

- Kind: effect; status: published; digest sha256:b3488dcd88c7cc37e069d1114241fc8bfe020404fae8963a241d888e62be9180
- Role: Full-frame text-art effect (Canvas 2D) with its own subject; the renderer can also restyle any other layer.
- Implementation: canvas2d; entry `src/ascii-portrait.js`
- Files: https://aipixelwizard.com/registry/ascii-portrait/1.0.0/<path> (`LICENSE`, `asset.json`, `demo.js`, `index.html`, `src/ascii-portrait.js`, `src/sculpted-head.js`)
- Manifest JSON: https://aipixelwizard.com/api/v1/visuals/ascii-portrait/versions/1.0.0
- Runnable demo: https://aipixelwizard.com/registry/ascii-portrait/1.0.0/index.html (open over http; it imports sibling packages by relative path)
- Dependencies (exact): `apw-runtime@1.0.0` (demo) — Demo harness and easing. src/ascii-portrait.js and src/sculpted-head.js have no dependencies.

## Exports
- `createAscii({ cellSize?, charset?, background?, ink?, dim?, contrast?, gamma?, glow?, fontFamily?, weight? }) => { grid(w, h), drawBuffer(ctx, w, h, buffer, opts?), drawField(ctx, w, h, field, opts?), applyToCanvas(ctx, w, h, opts?), caption(ctx, w, h, text, opts?), ramp, cellW, cellH }` — Canvas 2D character renderer. opts = { time, reveal: { start, duration, scramble } | null, clear }. caption() types text onto the same grid with a block cursor.
- `{ classic, code, binary, blocks }` — Built-in character sets; any string works too. Sorted at run time by measured ink coverage.
- `createSculptedHead({ hair?, zoom?, offsetY? }) => { render(cols, rows, { yaw?, pitch?, aspect?, light?, background? }) => Float32Array }` — Ray-marched marble bust as a brightness buffer, one ray per character cell.

## Parameters
- `caption` (text, default "hello. i am made entirely of text.") — Caption: Typed out under the portrait. Leave empty for no caption.
- `charset` (select one of classic | code | binary | blocks, default "classic") — Characters
- `cellSize` (number 8–24, default 12) — Character size: Height of one character in pixels. Smaller shows more detail, larger reads more as text.
- `ink` (color, default "#5dffa0") — Character colour
- `background` (color, default "#020a05") — Background: If it is lighter than the characters, the picture flips to ink on paper.
- `glow` (number 0–1, default 0.55) — Screen glow: Soft bloom around bright characters. 0 for a clean print look.
- `contrast` (number 0.6–2, default 1.15) — Contrast
- `decode` (boolean, default true) — Decode from scrambled symbols: The portrait starts as random characters that settle into the image.
- `turn` (number 0–90, default 40) — Head turn: How far the head turns over the clip, in degrees.
- `hair` (boolean, default true) — Carved hair

## Composition
- Inputs: 2D canvas context, a brightness buffer (or function, or the canvas's own pixels) and info.time in seconds.
- Outputs: Fills the canvas with the background colour and draws characters on it (pass clear: false to keep what is underneath). caption() draws on top.
- Coordinates: A grid of characters centred in the frame; grid(width, height) gives cols, rows and cell size. Buffers are row-major, top row first.
- Lighting: The bust has a key light from the upper left that swings with the turn, a rim light from behind and contact occlusion; brightness is mapped to character density.
- Timing: Pure function of time and parameters. Decode is already under way on the first frame and settles by about 2 s; the caption starts typing at 1.5 s at 18 characters a second.
- Works with: kinetic-title, vhs-camcorder, dither-animation, digital-rain, hologram
- Caption text is treated as private and is not stored in shared packs unless you choose to include it.
- Glyph shapes come from the viewer's monospace font. The character ramp is re-measured for whatever font is installed, so the tones stay right, but the exact look changes slightly between systems.

## Output and loop
- 1080×1080, 30 fps, 8 s, procedural
- Seamless loop: no — A reveal: the portrait decodes in, turns once and the caption finishes typing; it ends on the finished portrait with a blinking cursor.
- Verified 2026-10-10 in Chrome 154.0.8037.97 headless on Linux 7.0.0-38-generic (x64), Node 22.14.0 (Canvas 2D); deterministic: yes

## Use it
New project: Create the renderer once with createAscii, get its grid, and each frame build a brightness buffer of cols × rows (createSculptedHead().render does this for the bust) and call drawBuffer, then caption.

Existing project: Copy src/ascii-portrait.js. After you draw your frame on a 2D canvas (or copy a WebGL canvas onto one), call ascii.applyToCanvas(ctx, width, height, { time }) to redraw it as characters.

Reuse ideas:
- Draw your own footage or any 3D ingredient onto a 2D canvas each frame, then call ascii.applyToCanvas to turn it into text (for example Chrome Material Orbit or Black Hole).
- Use the decode reveal on its own as an intro: start with reveal.duration of 1 s over a logo drawn into a brightness buffer.
- Swap the bust for another subject: anything that returns a brightness buffer works, including Dither Animation's moon scene.
- Put Kinetic Title over the finished portrait for an end card, or run VHS Camcorder over it for a 1987 terminal.

## Limitations
- The bust is an idealised marble sculpture built from simple shapes, not a likeness of a real person; to turn a real face into text, draw your footage and use applyToCanvas.
- Characters take one colour ramp from background to ink; per-character colour taken from a photo is not supported.
- Monospace scripts only; the caption has no wrapping and no right-to-left shaping.

## License
MIT. Copyright (c) 2026 AI Pixel Wizard. Released under the MIT License. See LICENSE in this folder.

Provenance: Original code written for AI Pixel Wizard on 2026-10-07 by an automated agent (the daily trend routine), inspired by the ASCII portrait trend in code-made AI video: @johnknopf's ASCII self-portrait (https://x.com/johnknopf/status/2103698854399099057, indexed at https://claudevideo.org/videos/ascii-character-portrait-types-out-a-self-introduction) and @cherry_mx_reds's neon-to-ASCII restyle (https://x.com/cherry_mx_reds/status/2105816670896009224). Techniques: brightness-to-character mapping and signed-distance ray marching are long-standing public techniques; the head is built from smooth-unioned ellipsoids and capsules written for this asset. The character ramp is measured from the installed font at run time; no fonts, images, models or code from elsewhere are included. Uses the viewer's monospace font; no font files are bundled.

