Every cell of your image is measured for brightness, matched to a band in the character ramp, and drawn as that character in its own font, size and colour.
Canvas
- Drag
- Pan the artboard
- Scroll
- Zoom in and out
- Double click
- Fit artboard to the view
Keys
- ← →
- Fewer / more columns
- ↑ ↓
- Character size, all bands at once
- ⇧ + ↑↓
- Same, in larger steps
- F
- Fit to view
- I
- Invert tone
- G
- Open the Glyph Lab
- R
- Open the render dialog
- A
- Show or hide the timeline
- Space
- Play / pause
- K
- Set a keyframe at the playhead
- ?
- This panel
Proportion
Cell aspect never distorts the artboard — rows are derived to compensate. Tracking does, and rounding rows to whole cells leaves a little error besides. The readout under Tracking shows the artboard's ratio against the source's; Reset proportion solves for the tracking that matches them exactly, and Keep proportion holds it there as you work.
Colour
The gradient bar on the desk takes any number of stops — drag one to move it, click the bar to add one, × to remove. Spread by tone places each character's colour at its own threshold, so the gradient follows the image's tonal range; Spread evenly ignores tone and steps through the ramp one band at a time.
Size
Each channel strip has its own size slider. Master on the desk scales every channel at once and keeps their relative differences — set it back to 1 to undo.
Artboard
The artboard follows the grid unless you lock it. The artboard is what gets exported and the grid is fitted inside it, so the moment anything changes the grid’s shape — its columns, its tracking, a hand steering it — the two stop agreeing and the difference is painted in the background colour. Bands down the sides, or across the top. Unlocked, the artboard takes the grid’s shape and that can never happen. Lock size holds the exact rectangle instead and fits the grid into it, letterboxing and all, because a fixed output size is sometimes the whole point. Typing a width or a height locks it for you.
The artboard is what gets exported, in pixels. The character grid is fitted inside it and centred, so tracking and cell aspect can reshape the grid without changing the shape of the output. From grid takes the artboard's size from the grid as it currently stands — that is the one to press after tracking has changed things. Match source sets the height from the source image's proportions. Link sides holds the ratio while you type in either field.
Fonts
ASCHE ships with no faces of its own beyond the three your system provides. Fonts in the toolbar opens the library: drop .ttf, .otf or .woff files in and they are kept in this browser, loading every time you open ASCHE on this machine. Remove one and any channel using it falls back to System Mono.
Colour
Master scales every channel's size at once and keeps their differences. Size beside it replaces them: one number on every channel, which is what you want when the differences are the thing you are trying to remove.
Apply acts on the selected channels when there are any, and on every channel when there are none — one rule for Face, Size, Colour and the axes.
The desk has two colour modes. Solid picks one colour and Apply to all puts it on every channel — after which any single channel can still be changed from its own cap. Gradient spreads the stops across the channels, either by tone or evenly.
Glyphs panel
The Glyphs section of the left panel is a reading version of the Lab: pick a set and a face, and it shows every glyph that face actually carries, with a bar for its ink.
A face that cannot draw your characters fixes the ramp. Put a text face on a ramp of block elements and every glyph is drawn by the system fallback, which has no axes at all — so the sliders move a font that is not on screen and nothing happens, which looks exactly like the variable support being broken. Applying a face now checks what it can draw, and if it cannot draw any of the ramp the characters are swapped for the widest set it does carry. Thresholds, colours and sizes are kept; only the glyphs change, and it says what it did. If the face carries some of them but not all, the ramp is left alone and the axis bar says which are missing, because a partial swap would be a guess at which half you meant.
Or type the characters you want. A set and a face were the only two ways in, and neither helps when you already know which glyphs you are after. The Type field takes them directly: they keep the order you typed them in, they are measured in the chosen face like anything else, and a repeated character is dropped because the same glyph twice is the same band twice. Typing switches the set menu over so the two cannot disagree about what is on screen; clearing the field hands the panel back to the set.
The two orders are two different views. By ink runs lightest to heaviest and hides any glyph whose shape repeats one already in the list, because an identical shape gives you the same band twice and wastes a channel. As the set is written is the reading view and shows the alphabet entire — a letter is never dropped from it, however close it looks to its neighbour in a particular face.
This face’s own glyphs, at the top of the set list, reads the font’s cmap table and lists every character it contains — the same thing Illustrator’s Glyphs panel does. That is what makes an illustrative or dingbat face usable here, since its artwork sits at codepoints no preset set would guess. It needs a .ttf or .otf; WOFF2 is compressed in a way ASCHE cannot unpack.
Hand tracking
Poses need landmarks and landmarks need a model. The model is not inside this file — embedded it would add ten megabytes the browser parses on every launch whether you use it or not. It lives in hands.pack beside ASCHE.html: press Install pack once and it is kept in the browser the way your fonts are. From then on it loads from your own machine with nothing fetched, ever.
Your hand steers the picture; it is not in it. The tracker needs a camera. The artboard does not have to be one. Those used to be the same thing, which ruled hand gestures out for photographs and clips — most of the work. So the feed the tracker reads is its own now: if the source is already the live camera that one stream serves both, and if the source is a still image or a video the tracker opens a camera of its own and the artboard never knows about it.
How many fingers are up chooses what you are steering; the same sideways sweep does the steering. One finger steps the character set, two shifts the colour, three inverts, four shifts the colour. Counting raised fingers is the single most reliable thing hand tracking does, and nothing can be mistaken for anything else because each count is a different shape. Each is a dropdown, so any count can drive anything on its list.
Two poses are not counts and do not sweep. A fist brought toward the camera rides the grid: a nearer hand is a bigger hand, and the grid coarsens as you pull it in. A pinch is a dial for the character size — fingertips touching is the smallest type, as wide as they go is the largest, and every position between means the same size every time. Nothing is asked of the fingers that are not part of the gesture: an earlier version wanted the middle finger folded, which is one way to pinch and not the only one.
Wiggle all four fingers, in any hand shape, and the whole state rolls at once — the randomizer’s own roll, cross-faded in, and the artboard left alone.
Everything faces the camera. Not your face — the lens. Every pose is gated on the palm being square to it, so nothing fires while you are reaching for the mouse. Which winding counts as facing depends on your hand and your camera, so it is measured rather than assumed: hold your palm flat to the lens and press Calibrate once. It is remembered.
Teach shows, live, what the tracker reads from your hand and why, and records ten short takes of your own hands to a file so the numbers can be fitted to them. Every threshold in here came from a recording made that way rather than from reasoning, and the ones that were reasoned out were wrong: a palm held square to a lens reads about 0.41 of square-on, not 0.7; fingers curled toward the camera used to read as extended, which alone made the fist and the pinch impossible; and a hand at rest was pinching two frames in five.
If it stops, press Restart. It rebuilds the tracker and the camera from scratch and clears every counter. A supervisor is also watching: every frame races a two-second stopwatch, failures are counted rather than swallowed, and if results stop arriving for four seconds the tracker is rebuilt from the pack already in memory.
Undo and redo
Steering with your hands throws states past you faster than you can decide about them, and the good one is usually two gestures behind by the time you notice it was good. So every change is written down and you can walk back through them.
The desk counts too. It used to watch only the hand, so an hour of thresholds and colours would be stepped over in one go and land you back on whatever a gesture had last done. Now a state is recorded wherever it comes from: a fader, a number typed into a box, a colour picked, a face applied, a gesture, the randomizer landing. One drag of a fader is one step, and a number typed in is one step rather than one per digit — the entry is written when things stop moving, not while they are moving.
Raise the V and you are one step back. Sweep it toward your left to keep going back, toward your right to come forward again. Lower the hand and raise it for another step, the same ratchet the character step uses. Ctrl+Z and Shift+Ctrl+Z walk the same list, because a mouse is sometimes nearer than a camera, and the two arrows on the desk do the same thing.
What is stored is the whole state a keyframe stores — characters, colours, grid, sizes, axes — so a step back restores a moment rather than a setting. Sixty of them are kept. Reaching a new state after stepping back throws the forward tail away, the way every undo stack always has; merely touching a control and changing nothing does not, so clicking around while you decide costs you nothing.
Camera
Camera in the toolbar makes a webcam the source. Everything downstream is unchanged — grid, tone, ramp, colour, artboard and keyframes all apply to the live picture the way they apply to a still. Mirror is on by default, because a camera you are looking into should behave like a mirror or you cannot aim. If the machine has more than one camera a picker appears beside it.
Record captures the live view using whatever Render video is set to, so there is one set of settings rather than two that can disagree — MP4, GIF, WebM, or a PNG or SVG sequence. Recording runs until you press Stop or reach the length in the dialog; stopping early keeps every frame captured up to that moment. A live source has no length to scrub, so the timeline is put away while the camera is on and the render dialog asks for a maximum length instead of in and out points.
The browser asks permission the first time. Opened straight off your disk the page has no persistent origin, so it asks again each session; served from a local web server it remembers. Nothing leaves the machine either way — there is no network code in this file.
Zoom
Zoom in the Grid panel samples the same frame more coarsely. Half the columns, cells twice as wide, characters twice as big, the same rectangle — so the picture loses resolution and each character stands for a larger patch of it. That reads as zooming in, and it is the move you used to make by pulling the column count down, before the artboard started following the grid and made that shrink the output instead.
The artboard does not move. The frame is what the column count asks for at zoom 1, and the artboard follows that; zoom only changes how many samples are taken inside it. The cells are sized by dividing the frame rather than by multiplying up, so the grid fills the frame exactly however the rounding falls and no background ever shows down one edge.
It is part of a keyframe, so it sweeps like everything else — a slow coarsening across a clip is the whole picture dissolving into its own characters — and the randomizer reaches for it under Grid.
Variable faces
Add a variable .ttf or .otf and ASCHE reads its fvar table: the axes it offers, their ranges, and the instances the designer named. A slider for each appears in the desk bar, and the named instances in a dropdown beside them. A static face shows nothing there.
Axis positions are per channel and are part of a keyframe, so weight, width, slant or any custom axis sweeps between two keys the way tone and colour do. That is the point of it here — a ramp whose characters thicken across the frame rather than swapping.
Canvas has no way to set an axis directly, so each position is registered as its own face behind the scenes. Positions are rounded to keep an animation reusing a handful of them rather than minting one per frame, and the cache is capped. A position still loading draws at the face’s default for one frame. The SVG export carries the real axis values, so vector output is the instance you see and not the default weight.
Safari takes a different road. That trick above — a face registered at a named axis position — is quietly ignored by Safari, which is why the sliders moved there and the picture did not. ASCHE now measures which route this browser honours, once, by drawing the same glyph both ways and comparing the ink. Where the registered position works, nothing changes. Where it does not, the axes are said in the font string instead, which Safari does honour — but CSS only has words for four of them: weight, width, slant and italic. Any other axis a face offers is shown struck through and switched off there, and the randomizer leaves it alone rather than spending a sweep on something that cannot move. On the channel strip the little glyph preview is HTML rather than canvas, so it shows every axis correctly in every browser.
Route, at the right of the axis bar, is the measurement’s answer and your override. auto is the measurement; instance and CSS force one road or the other; off stops pretending the axes can move at all. A measurement can be wrong — a face that has not finished loading draws the same at both ends of an axis, which looks exactly like an axis that does nothing — so if auto has clearly got it wrong, say so here and it is remembered. The label beside it says what was measured.
The ink measurement that orders your ramp is taken at the face's default instance. If you sweep weight a long way, the tonal order was chosen for the weight you measured at — re-measure in the Lab at the weight you care about if the ordering starts to matter more than the motion.
Selecting channels
Click a channel's head — the strip along its top with the number and the tone meter — to select it, drag across the heads to take a run, shift-click to extend, ⌘/ctrl-click to pick one out. All and None are in the desk bar. Delete, or the Delete key, removes everything selected at once; clearing the whole desk leaves one blank channel to build on.
Replace swaps the selected channels for whatever is picked in the Glyphs panel. When the two counts match it is a straight morph — each channel keeps its threshold, colour and size, and only the character changes, so a ramp you have spent time tuning can change alphabet without being rebuilt. When they don't match, the selected block is replaced outright and the tones are respread across the span it held.
Right-to-left scripts
Arabic, Persian, Urdu, Hebrew and Syriac sets lay their cells out right to left, so the first letter of the alphabet is the top-right cell and each cell’s ink meter fills from the right. Digits stay left to right, because Arabic and Persian numbers are written that way even inside right-to-left text.
The rule is that surfaces you read follow the script — the Glyphs panel, the Lab’s grid, the boxes you type characters into. Surfaces that are instruments do not: the desk, the faders, the tonal ramp preview and the timeline always run light-to-dark and earlier-to-later left to right, because there the direction means tone and time rather than reading order.
How ink is measured
Each glyph is drawn into its own cell with half a cell of clear space on every side, and the ink is counted over the whole of that. The margin matters: marks that sit above cap height — the madda of آ, the diaeresis of Ё, the breve of Й — fall outside the cell itself, and measuring only the cell would make each of those letters identical to its bare form.
Alphabetical order
Every language set is written out in that language’s own alphabetical order, not walked through Unicode. The two are not the same thing: Persian breaks in five places — پ چ ژ گ are encoded far outside the base Arabic range, and و comes before ه — Urdu breaks in ten, Russian’s Ё is encoded before А, and Tamil’s block runs ல ள ழ வ where the alphabet runs ல வ ழ ள. Letters, marks and digits are kept in separate sets rather than jumbled together.
Click glyphs to pick them and press Add picked, or drag a glyph straight onto the desk — a green caret shows where it will land, and it takes a threshold halfway between its new neighbours so nothing you have already dialled in gets pushed around. Drag a channel by the grip beside its number to move it along the desk.
Presets
Save preset writes a .json holding the ramp, grid and tone settings — fonts you added are stored inside it, so a preset opens the same way on any machine.
Load preset also reads the old Processing .xml settings files. Character count no longer has to match: the ramp resizes itself.
Animate
Animate opens the timeline under the canvas. It drives two kinds of motion, and they combine.
From a video. Drop an .mp4, .mov or .webm in. The transport plays, pauses and steps it with the character effect applied live, and the ruler scrubs. Every setting stays editable while you scrub, so you can dial the ramp in on the exact frame you care about.
From keyframes. Open a JPG or PNG, set a length, then move the playhead, change anything at all — grid, tone, background, colours, sizes, the whole character set — and press ◆ Keyframe. Move along, change something else, keyframe again. Between two keyframes the grid, tone, background, thresholds, sizes and colours all glide; characters cut over at the midpoint. Change the number of bands and it cuts rather than blends.
Drag a diamond to retime it, double-click to delete it, and set how each one eases into the next. Duration in the timeline bar sets how long a still-image animation runs — up to an hour; a video sets it from the file. Keyframes are saved inside the preset.
Randomize
Randomize writes an animation rather than shuffling one state. A shuffle throws a new state at the screen and the result is noise; a route through a handful of states, with the interpolator doing the work between them, is something to watch. And because it writes real keyframes, everything that already works on keyframes works on it: scrubbing, easing, the character wipe, the dip, and export.
Two things make the difference between a randomizer and a random number generator. Nothing varies unless you tick it — randomising the colour of a ramp you spent an hour on is vandalism if you only wanted the weight to move. And each dimension is randomised the way it wants to be: colour picks one hue and one harmonic relationship and spreads it down the ramp, so every frame is a scheme rather than a bag of hues; the grid steps between round column counts instead of landing on 173; size moves the master and leaves the differences between channels alone; and the axes pick one or two of whatever the face offers and sweep them, because five axes jittering at once is mud.
Range is how far it is allowed to go from what you built. Seamless loop makes the last keyframe the first one again, so it can run round. The first keyframe is always the state you are already in, which makes the whole thing a departure from your work rather than a replacement for it — and the artboard is never touched, so the proportion of the output cannot change underneath you.
A video brings its own length, so Seconds locks to it: keying five seconds of animation across a twelve-second clip would leave seven seconds held on the last frame. A live camera has no playhead at all, so there the same states are walked on the wall clock instead — a chain of cross-fades, each handing over to the next when it lands, round and round. Same states, same easing, same wipe; only the clock is different. Press Randomize again to stop it.
Changing character set mid-animation
Swapping the whole ramp between two keyframes would normally snap. Instead each band flips its own glyph at its own moment, and that moment walks down the ramp: Wipe from darkest starts at the heaviest character and works toward the lightest, Wipe from lightest does the reverse, and Cut is the old all-at-once behaviour. Ramps of different lengths are resampled to a common count first, so going from six characters to twelve still glides.
Dip hides the substitution inside a change of size: each character shrinks into its own swap and grows back out, so the old glyph has all but vanished by the moment it is replaced. Around 60–80% reads as a proper morph. On a wipe the dips overlap slightly and travel as one wave down the tonal range; on a cut every band dips together and the whole image pulses in and out. Bands whose character isn't changing hold their size.
How long a render can be
Every frame is held in the tab until the file is finished, so the ceiling is memory rather than policy. A shared page stops at 900 frames — 75 seconds at 12 fps — which keeps it from locking up a stranger's browser. A local copy of ASCHE has no cap and will go to 20,000 frames. The render dialog always shows the ceiling and what it works out to at your frame rate.
Render video walks the in/out range one frame at a time. MP4 encodes H.264 where the browser has it, otherwise VP9 or AV1 in the same container, and tells you which. GIF builds its palette from your own ground and character colours, so there is no flicker between frames.
Export
PNG renders at whatever multiple you choose. Vector SVG writes real text with the fonts embedded — open it in Illustrator and save as PDF for print. .txt gives you the plain characters.
The render dialog also writes sequences: a numbered PNG sequence or a vector sequence of one SVG per frame, both zipped. Sequences only save from a local copy of ASCHE — a shared page isn't allowed to hand over a .zip.