Basic Cohub abilities. Most agents already have these mounted; listed here for reference.
Platform Published by the Neta team.
Official Made by the Neta team.
Verified Reviewed by the Neta team.
Community Shared by a Cohub user. Not reviewed — read SKILL.md before you use it.
ascii-art-image
Verified@siosan/ascii-art-image
Use it in Cohub
Paste this into your agent's chat — it fetches the skill and follows it.
Use the skill: ascii-art-image. Run `npx -y @neta-art/neta-search-cli@latest skills use @siosan/ascii-art-image --via site` and follow what it prints. Read its complete output, redirecting it to a temporary file first if necessary, and resolve relative paths from the skill directory it prints.
npx -y @neta-art/neta-search-cli@latest skills use @siosan/ascii-art-image
Summary
Turn one picture or short video into character art made of letters, dots, pixels or bricks, still or moving.
Send an image, a short video, or point to a character card or finished work. It comes back rebuilt from characters, braille dots, halftone, dither pixels or bricks.
Looks include classic text art, Matrix-style falling code, binary, braille, halftone, Game Boy pixels and brick mosaic, with an optional old-monitor glow.
A video turns frame by frame, so the subject really moves. A still can loop with twinkling glyphs, a slow zoom or sway, or stay a plain image.
You can ask for a still to come to life, such as a wave or a turn. That makes a short clip first and uses your video credits.
Works best with one clear figure against a simple background, or a place with big shapes like doors, light and water.
SKILL.md
# ASCII Art Image
Hand over one reference image or short video and get back a finished piece rebuilt out of characters, dots, pixels or bricks: the same subject and composition, recognisably the same picture, in a hard graphic texture. From a video every frame is converted, so the subject itself moves (this is how character-art generators animate a subject); from a still whose subject should move, the video model first turns the still into a short looping clip (first and last frame = the reference) and that clip is converted; otherwise a still's glyphs move in a seamless loop, optionally with a code-only camera move. Everything is computed by `scripts/ascii_art.py` from the reference's own pixels (glyph ramps, structure-tensor contour glyphs, gradient lookup tables, ordered dither, CRT post-processing, per-cell motion); the only model call is the optional clip step (`animate`), which spends the user's video credits; everything else is exact, repeatable and free.
Read this file fully before writing any file or running the renderer. Resolve relative paths from this skill's directory; `$SK` below means this directory.
## Language
Reply in the user's language, whatever language this file is written in — in every message of the run, above all the final hand-over; translate what the scripts print, never paste it. Text the user sees in the deliverable (the Work title, the board group name, the hand-over lines) follows the user's language; names the user typed stay as they wrote them.
## When to use / when not
Use when the user brings or points at one still image or one short video and wants it remade as ASCII / binary / braille / halftone / dither / tile / brick art, moving or still, with or without a CRT look.
Not for:
- Drawing a new picture from a text description with no reference image: this skill restyles an existing image. Say so, ask for an image or a card that has one, and stop.
- A live, adjustable generator page: say this skill delivers a finished image, and stop.
- A painterly or AI restyle ("make it look like Ghibli"): say this skill makes character / dot / pixel / brick art, and stop.
## Inputs
| Item | Required | Counts as given when |
|---|---|---|
| Reference image or video | yes | The user uploads an image or a video (mp4 / mov / webm / animated GIF), pastes its URL, names a card in this world whose cover exists, or names an image or video Work in this world. "My character" when the world has exactly one character with a cover also counts. A card with no image yet, "something cool" or "any picture" does not. |
| Look | no, default preset `classic` | Read from the user's words with `references/styles.md` (preset first, then flags). Several looks named = one Work each. |
| Subject motion | no | Asked for when the user wants the subject or scene itself to move: "make it come alive", "动起来", "let her wave / turn / walk", "the gate opens". Then the still goes through `animate` first. The action: what the user said; if they only said "move", choose one big action that fits the card (a character: one gesture true to their personality; a place: its largest elements — doors, light, water, banners) and name it in the hand-over. Do not ask. Plain "GIF / animated / 动图" without asking for the subject to move = free glyph motion, and mention in the hand-over that the subject itself can be brought to life. |
| Moving or still | no, default animated | A video always moves with its footage (first 5 s unless the user names a span → `--start`, `--seconds` up to 10). A still loops with the preset's glyph motion; "zoom in / pan / sway / breathe / make it move" on a still → `--camera push|pan|sway|breathe`. Still output only when the user says still / static / wallpaper / print / no animation (`--still`; for a video, its first frame). |
| Palette, motion, detail, CRT, glyphs, other knobs | no, preset defaults | Read from the user's words with `references/styles.md`. |
If the reference image is missing or ambiguous (two cards match the name), ask once in one short message (which image, or which of the two) and stop. If it is given, do not ask anything else and do not wait for a go-ahead: pick the preset and flags from the user's words (defaults otherwise) and build through to delivery. When you choose something the user did not say, name it in one clause in the hand-over.
## Steps
`$RUN` = `_runs/ascii-art-image/<YYYYMMDD-HHMMSS>[-<preset>]/` under the workspace root, one folder per Work. If the user or the environment asks for outputs in a particular folder, pass that folder as `--run`. Never move, rename or delete a run folder afterwards.
1. **Find the reference.**
- Upload: use the path it arrived at (download a chat-upload URL first with `curl -fsSL -o uploads/<name> '<url>'`).
- URL: pass it as is.
- Card: `node /mods/neta/neta atom list`, take the card's `id` (its `world/…/*.md` path) and pass that path; the script reads the first image in its `media`.
- Image Work: `node /mods/neta/neta work list`, pass its `path` (`works/<id>.json`).
1b. **Bring a still to life** — only when *Subject motion* was asked for and the reference is a still. Costs the user's video credits (one clip, about 5 s):
```bash
python3 "$SK/scripts/ascii_art.py" animate --source "<same reference>" --run "$RUN" --action "<one big action, in English>" --work-title "<subject> · <'motion material' in the user's language>"
```
It submits through `scripts/gen.mjs` (the user's video model first, then fallbacks; busy → retry or next model; nothing is charged twice) with the reference as first and last frame (so it loops), waits up to 8 minutes, and writes `$RUN/motion.json` (`status`, `taskRunId`, `model`, `workPath`, `mediaPath`). Pass: `{"ok":true,"status":"ready","mediaPath":…}` — then use that `mediaPath` as `--source` in step 2. Never call `neta generate` yourself.
- `"pending": true` (`generating` or `busy`) → run the same command again with the same `--run`; it only polls and never resubmits. Do not ask the user or offer other routes. Still pending after a third call: tell the user the clip is still generating and stop.
- `fellBackFrom` set → say in one line which model made the clip (its price can differ).
- `failed` after acceptance → tell the user, and run `animate` once more with a new `--run` folder only if they want it; `blocked` (credits / permission) → say so, offer the free glyph-motion version, stop.
2. **Prepare** — writes the user's choices into `$RUN/config.json` before anything is rendered; build only from that file:
```bash
python3 "$SK/scripts/ascii_art.py" prepare --source "<path | URL | card .md | work .json>" --run "$RUN" --preset <preset> [flags from references/styles.md] [--still]
```
Pass: prints `{"ok":true,…}`; for a video it has extracted the frames into `$RUN/frames/` (`video` in the output). `could not read the reference` / `the card has no image yet` → tell the user what is missing and stop (see *When to stop*).
3. **Render** — `python3 "$SK/scripts/ascii_art.py" render --run "$RUN"` (10–40 s for an animation, about 0.4 s per video frame). Writes `render.gif` + `poster.png` (first frame) + `render.mp4` (with the video's own sound when it has one) when animated, or `render.png` when still; `render.txt` for ascii and braille; a uniquely named copy for import (`files.export`); and `render.json`. A GIF over 12 MB is re-rendered smaller by the script itself (`fitted` in its output).
4. **Check** — `python3 "$SK/scripts/ascii_art.py" check --run "$RUN"` → `$RUN/qa.json`. It passes when the render and export exist, match the configured style and motion, keep the reference's aspect ratio, are not blank, still correlate with the reference's tones (recognisable), visibly differ from it (stylised), and, when animated, have the planned frame count, actually move, loop without a jump (stills) or follow the footage (videos), and stay within the size limit; plus one text line per row for ascii / braille.
On a failure, fix once by the check that failed, then prepare + render + check again:
- `not-blank` or `recognisable` → too flat or too dark: add `--contrast 1.4` (and `--brightness 0.1` for a dark source), or lower `--detail` for a tiny source.
- `stylised` → raise `--detail` back to the preset's.
- `glyph-only` → a look that shows the original picture under the glyphs was used without the user asking to keep the picture: go back to a pure glyph preset.
- `moves` on a clip made by `animate` (its motion came out too small) → re-run prepare with the same clip plus `--motion twinkle --amount 0.4` (free), and say in the hand-over that the generated movement was subtle; do not generate again unless the user asks.
- `moves` → raise `--amount` to 0.6. `loops` → re-run render. `gif-size` → add `--width 640`. `follows-footage` → the span barely moves: pick a livelier `--start`.
- anything else → re-run `prepare` with the same flags.
A second failure: tell the user it needs another pass, with the failing check in plain words, and stop without registering.
5. **Look at it.** Open `$RUN/contact.png` (animated: three moments of the loop side by side) or `$RUN/render.png` (still) with the read tool. Compare with the same moments of the source, not just its first frame: a clip's subject often enters later, and an empty opening is the footage, not a fault. The subject must read at a glance where it is on screen. If it does not, fix once, in this order: raise `--detail` by about 1.5×; then `--edges 0.8`; then a light page (`--palette paper`) or `--local-contrast 0.5`. Never switch to a look that shows the original picture under the glyphs (`gradient`, `binary-gradient`, `--over-image`) to make the subject visible — that is no longer character art; those looks are only for users who ask to keep the picture, and `prepare` refuses them without `--keep-picture`.
6. **Register, place and hand over** — see *Deliver*.
## Deliver
Kind: **image** Work, one per look — an animated GIF by default, a PNG when still — registered from the local render (no generation).
1. Register:
```bash
node /mods/neta/neta work import image --file "<files.export from render.json>" --prompt "<one line: the subject, the look and the motion, e.g. 'The Shelf gate as falling green binary code'>" --work-title "<subject> · <look name>"
```
`<subject>` = the card's or Work's name, or a two-to-four-word description of an uploaded image, in the user's language (keep a name as the world writes it). `<look name>` from `references/styles.md` (*Title suffix*), in the user's language. A re-render after feedback is a new import with the same `--work-title` rule. Pass: `{"ok":true,"data":{"workId":…,"workPath":…}}`.
2. Place it on the Board, one call per request — the character-art Work only, never the motion material from `animate`:
```bash
node /mods/neta/.agents/skills/board-ops/scripts/place.mjs --title "<group name, user's language>" [--near "<card id when the reference was a card>"] --work "<workPath from step 1>"
```
A world with no Board answers `path_not_found`: skip this step and go on.
`--work` takes `workPath` exactly as printed (not the `workId`).
3. Hand over: the Work with an `interaction-components` link card, `target: {"type":"work","workId":"<workId from step 1>"}`. Also link, as files, `render.mp4` (with the source's own sound, for platforms that want video) when it exists and `render.txt` for ascii / braille. When the user asked to keep the original sound or for a video, the GIF is still the registered Work and the MP4 is that file link — do not deliver the MP4 alone instead of the Work. Then one or two lines: the look, the motion, and any default you chose. A Markdown image of the file alone is not a hand-over.
When `animate` ran, say in one line that bringing the picture to life used video credits. Do not link, embed or place the motion material: it is an ingredient, not part of what is handed over.
Before handing over, `qa.json` must say `"pass": true` and the Work must appear in `node /mods/neta/neta work list`.
## Out of scope
- Do not change world settings (WORLD.md config, visual style, slug) and do not delete files that existed before the run.
- Do not modify memory, character cards (including their covers), installed scripts or skills, existing Works or roleplays. The reference stays untouched; the result is always a new Work.
- Make only the Works asked for: no web page, card, cover change or extra variations, and do not act on your own next-step suggestions. The MP4 stays a file in the run folder; it is not registered as a second Work. The only other Work is the motion material `animate` itself creates, which is an ingredient: never placed, linked or shown as a result.
- No generation calls outside `animate`, and `animate` only when subject motion was asked for: never to "improve" a free result, never more than one resubmit after a failure. Do not edit the script to change a look — use its flags.
## When to stop
- No usable reference image (none given, the named card has no image yet, the file is not an image, the URL does not load): say what is needed (an uploaded image, a link, or a card that has a picture) in one message and stop. Do not generate or draw a substitute.
- Two cards match the name: ask which one, once, and stop.
- Asked for a live generator page or a new drawing: say this skill turns one image or video into character / dot / pixel / brick art, and stop.
- A video longer than 10 s: use the first 5 s (or the span the user names) and say so in the hand-over; do not ask.
- Only `animate` costs credits; a run blocked for credits there: say so, offer the free glyph-motion version, and stop.
## Gotchas
- Judge a clip from `contact.png`, never from frame 0: in a real run the source clip opened on an empty room, the agent decided the subject had vanished in `rain`, and switched to a picture-under-glyphs look — the result was the original video with code over it.
- `rain` keeps the picture's structure by brightening the falling trails rather than thinning everything else; a dark subject on a dark background reads as a silhouette of dense code.
- A still made to move needs big motion: a clip where only small details move (icons, check marks) converts into character art that barely moves, because small details vanish at 100–150 columns. `animate` asks the model for large motion; write the `--action` the same way (whole pillars, arms, doors, light — not small icons).
- People work best as subjects: one full-body or half-body figure, clear against its background. Write the `--action` as one or two large body movements (wave an arm high, swing a prop onto the shoulder, turn to the camera, a small hop); facial expressions alone are lost at 100–150 columns.
- A figure on a white or very light background goes onto a light page with dark glyphs under `auto`; under `rain` or other dark looks it becomes a dark silhouette in falling code. Both read well — choose by the user's words, not by the background.
- `animate` pins the last frame to the reference so the GIF loops; `--no-loop` only when the user wants a one-way action (e.g. a door that ends open).
- When the opener names a card, take the card even if Works with similar titles exist (e.g. "<card> · MV" Works); say which one you used in the hand-over.
- Pass a card as its `.md` path, not its `media` path: the script resolves the cover itself, relative to the card, and stops cleanly when the card has no picture yet.
- Import `files.export`, never `render.gif` / `render.png`: `work import image` copies the file into the world's media under its own filename, so a fixed name would land on the last run's file.
- `classic` with the default `auto` palette puts a light picture on a light page (dense dark glyphs where the picture is dark) and a dark picture on a dark screen. A light picture forced onto a dark screen (`--palette original`, `console`, …) reads as a negative — intended for terminal looks, not a bug.
- GIF frames compress only where pixels stay the same: per-frame noise is costly. The script keeps grain and scanlines fixed and steps flicker every 3 frames. Over 12 MB it first lowers the GIF to 8, then 6 fps, and only then shrinks the picture (lowering detail with it, so glyphs stay readable); the MP4 always keeps every frame. `rain` and `monitor` over moving footage are the heaviest (`fitted` in render.json shows what it did).
- Brick colours are matched in Lab space; one-brick-wide coloured lines blend with their neighbours. Raise `--detail` when small coloured details matter.
- `--detail` and other knobs are clamped to safe ranges; a value outside them is clipped, not an error.
- A video's tone levels are measured once on its first frame and kept for every frame, so the picture does not pump brighter and darker; a clip that starts on a black frame should get `--start` past it.
- Video frames are converted one by one (glyph atlas cached), so a 5 s clip at 12 fps is 60 renders; the GIF keeps the motion, the MP4 also keeps the sound.
- `render.txt` for braille uses Unicode braille characters; it shows correctly only in a font that has them.