Scribbler, 2025

vSketch, Axidraw

Research and Exhibition: Algorithmically generated asemic (without meaning) script mimicking the structures and style of handwriting

Exhibited at The Frame Gallery, April 2025

Exhibited at The COOP, April 2026

This piece is a speculative research project into the nature of written human language. I analyzed large samples of text to determine the consistent graphical elements and data encodings that emerge from written languages across cultures. I then built a machine that interfaces with a pen plotter, capable of writing generative asemic (without meaning) script in realistic human handwriting. This results in what appears to be pages of human-written text in an unknown language but reveals itself to be instead machine-written text written in no language at all.

I collaborated with educational YouTube content creator PurpleMind to produce a video exploring the mechanics behind generating realistic asemic script and simulating handwriting with machines. The video, sponsored by Brilliant.org, was crafted to be accessible to viewers of all knowledge levels.

You can generate your own plotter-ready asemic script by downloading the project from my GitHub.

Behind the Project

Building a Generator

June - September 2024

The question behind Scribbler is: what is it, exactly, that makes a page of writing read as writing? Not what makes it legible - what makes you recognize it as language at a glance, from across a room, without reading a word.

The obvious modern approach to exploring this question is to train something. Feed a model enough handwriting and let it produce more. I did not want to do that. A model that has learned to imitate handwriting cannot tell you why its output looks like handwriting. The whole point was to find the rules, which is antithetical to the black box AI model.

A title card reading: What about a simpler way?

Animation from the PurpleMind video.

So the project began by hand, in June 2024, as a set of small programs in vsketch. Everything in it rests on one observation about the Latin alphabet:

The full lowercase and uppercase Latin alphabet

Animation from the PurpleMind video.

Nearly every letter in the alphabet is a vertical stroke with things attached to it. A t is a stroke with one crossbar. An f is a stroke with a crossbar and a hook. An E is a stroke with three branches on one side. An L is a stroke with one branch at the bottom. Strip the letters down to that skeleton and much of the alphabet survives the reduction almost intact.

Real letters above, their reduced spine-and-rib structures below

Actual letters, and the basic structure underneath them. From the PurpleMind video.

That skeleton is always the same object: a vertical spine, with a number of horizontal ribs extending from it. Letters differ only in how tall the spine is, how many ribs it has, and where along the spine they sit.

The same structures with their vertical spines highlighted in yellow

The vertical spine of each structure, in yellow. From the PurpleMind video.

The spine is divided into a small number of discrete heights - the vDiv parameter, usually on the order of four or five - and a rib may attach at any of them. A second spine can be added alongside the first, with its own start and end height to allow for more character diversity and loops. That is the entire vocabulary of a character: one spine, sometimes two, and a scatter of ribs at fixed positions along them.

The generated character with ribs attached along its spine

Rib positions, and the character they build. From the PurpleMind video.

That design arrived in four steps, each of which survives as its own program in the repository. What follows is a crop of a page from each, in order, with the change that produced it.

The first version had the spine and the second spine, but the marks along them were single dots rather than ribs, and every number in it - the heights, the spacing, the number of divisions - was written directly into the code.

A sparse page of slanted spines with dots scattered beside them

1. Dash and dot. Spines with dots along them, on a fixed grid.

Replacing each dot with a short horizontal line turned it into the rib described above, and the hard-coded numbers were lifted out into named variables - vDiv, rib length, rib probability. From this point the script could be tuned from this growing set of parameters.

A denser run of spines, each with short horizontal ribs branching off it

2. Dash and accent. Dots become ribs, and the constants become parameters.

Both of those pages are a single unbroken stream of characters. Real writing is not: it is broken into words, and the words are of uneven length. So the next version grouped characters into words, drawing each word's length from a skewed distribution and inserting a wider gap at each boundary. That distribution is discussed in greater detail further down.

The page that came out has the shape of a left to right writing system - ragged right edge, visible word rhythm. However, it was still missing something quite important. Because every character was still generated fresh from the random source, it was quite rare to see the same character randomly generated more than once. Real scripts work the opposite way: a small set of shapes, used over and over, and even languages with very large sets of characters have a certain subset that are used at a high frequency.

Spines and ribs grouped into word-like clusters with spaces, but every character different

3. Non-alphabetic words. Characters group into words, but the same character rarely appears twice.

An alphabet made of random numbers

The fix is to stop generating characters at the moment of drawing them, and instead generate a fixed set once, then build words by picking from that set.

How that set gets stored is the part I am most pleased with, because the characters are not stored at all. There is no library of glyph shapes anywhere in the program. The drawing routine always asks the same questions in the same order:

1.How tall is this character, as a whole number of spine divisions?

2.Does it get a second spine at all?

3.If so, where does that spine start and end?

4.Then, one question per available rib position: is there a rib here?

Give it the same answers and it draws the same character, every time. So a character is stored as its answers - a list of random numbers, read off in order as the questions come up. The list is the letter. An alphabet is a set of those lists, generated once when the page begins; regenerate them with a different seed and you have an entirely different written language.

A full row of generated characters forming an alphabet

A generated alphabet. From the PurpleMind video.

The fourth program implemented a basic version of this idea that I would then refine over time. You can now see consistent repeating characters rather than random unrelated shapes.

Word-like clusters built from a small set of recurring character shapes

4. Alphabetic words. Characters are drawn from a fixed set, so shapes recur.

There is one deliberate omission: a character's slant and its size are not read from its number list - they come from the global random source at the moment of drawing. So every instance of a given letter has identical structure but is never quite identically drawn. Which is exactly the property real handwriting has: the same letter, twice, may have the same structure, but is not the same mark.

On September 9th 2024 I put the project under version control and began plotting my generated characters with my axidraw. This is when my idea of creating a generative asemic script and plotting it to look like handwriting full coalesced in my mind.

Early Axidraw asemic writing timelapse

Putting It on Paper

September - November 2024

A pen plotter draws an SVG, and the usual last step before plotting is to optimize the path order so the pen wastes as little time as possible traveling between strokes with its nib lifted. vsketch does this through vpype, and the early version of Scribbler used the full chain: linemerge linesimplify reloop linesort. That last command is the optimizer - it reorders every path on the page into something close to a shortest tour. You can see the results of this optimizer in the video above.

It makes the plot faster but it takes away from the piece. The machine hops around the page plotting strokes in a seemingly random order according to this optimization algorithm. The script is designed to look like a language written left to right, top to bottom, and characters are even generated in that order. To me, the point of this piece is to recreate not just the end result of human writing, but to also recreate the process of human writing.

So I dropped linesort. The paths stay in the order they were generated, which is reading order, and the machine writes the page the way a person would - a little slower, but following the sequential logic of real writing.

The other change between the first plots and this one is mechanical. My axidraw is precise to at least a tenth of a millimeter, and in the case of Scribbler, this is actually a bad thing. I wanted to create handwriting that looked like it could have been written by a person, and a person does not consistently have such a high degree of precision. I will later discuss many of the computational sources of noise I added to offset this precision, but there was a quite elegant and almost stupid improvement I could add right away: a loose spring. Instead of rigidly attaching my pen to the axidraw, I attached the pen to a spring, and that spring to the axidraw, producing an underactuated system. Adjusted properly, the spring produces a soft organic wobble - the pen wanders very slightly, the way a hand does.

Scribbler plotting asemic script in reading order

November 2024. Writing left to right, top to bottom, with the spring tuned for an organic wobble.

Making It Read as Language

October - November 2024

A page of correctly-formed characters still does not read as writing. What sells it is everything above the level of the character: how many distinct symbols there are, how long words run, how lines sit relative to each other, and how imperfectly the whole thing is executed.

How large should the alphabet be?

Too few characters and the repetition is obvious - you start seeing the same shape three times in a line and the illusion dies. Too many and nothing ever repeats, which reads as noise rather than language.

The useful distinction is between phonographic scripts, where characters stand for sounds and the set stays small, and logographic ones, where characters stand for words or concepts and the set is enormous. English runs 26 letters, Georgian 33, Armenian 39; written Chinese has on the order of fifty thousand characters. Sticking with our latin inspiration, Scribbler sits at the phonographic end, generally around thirty.

A comparison of phonographic and logographic scripts, showing character-set sizes for English, Georgian, Armenian and Chinese

From the PurpleMind video.

How long should words be?

Word length is not uniform in any language and it is not normally distributed either - it is heavily skewed, with a hard floor at one character and a long tail of rare long ones. Scribbler draws each word's length from the absolute value of a normal distribution, which gives exactly that shape: mostly short words, a scattering of long ones, nothing of length zero.

The standard deviation of this normal distribution is a parameter in the code, so the script can be tuned toward the short-word feel of English or the longer compounds of German.

A skewed histogram of word lengths, with average word lengths for English and GermanA generated page of asemic words beside a settings panel

Animation from the PurpleMind video.

Lines that avoid each other

This is my favorite mechanism in the whole program, and it came directly from staring at handwritten pages.

When you write by hand on unruled paper, your lines are not evenly spaced and they are not straight. However, they tend not to collide. If a letter on one line has a long descender, the line beneath it quietly bends out of the way; if the line above happens to sag, you write a little lower. You do this without thinking about it, and it is a large part of why handwriting looks organic rather than typeset.

I reproduced this same mechanism for Scribbler. The program keeps a record of the vertical position of every character on the previous line. When it places a character, that character's vertical offset is the maximum of two things: where the previous character on this line sat, plus a small random nudge, and how low the line above came down at this exact horizontal position.

The consequence is that lines physically push each other down. A descender on one line dents the line below it. A short stretch lets the next line ride up. Collisions cannot occur - the same rule that generates this up-down wiggle is the rule that prevents the overlap, all according to the same mechanisms implicitly at play in true handwriting.

Imperfection as a parameter

The rest of the realism is a set of controlled defects, each with its own knob: how much a character's vertical strokes slant, and how much that slant varies between characters; how much letter size varies within a line and between one line and the next; how far each new line starts left or right of the last; how much characters shrink as the page goes on, the way a hand tires or compresses to fit the remaining space.

A settings panel with character height, tilt and spacing controls, beside a page of slanted asemic script

Animation from the PurpleMind video.

Many of my goals with Scribbler are the opposite of what you may normally want from a machine.

A slide contrasting what you normally want from a robot, consistency and precision, with what this project wants, inconsistency and imprecision

Animation from the PurpleMind video.

Style as Meaning

November 2024

The script is meaningless by construction - there is nothing to read, and no amount of study will yield a translation. But the page is not meaningless. Set the lines far apart, keep the characters upright and the margins generous, and you communicate a certain style, or even emotion, of writing. Compress the spacing, tilt everything, let the line starts scatter, and add passages struck through, and you get something a reader understands as chaotic, rushed. Make the characters huge and wobbly and irregular and you imply a hand not able to write well.

Without being able to read a single word, the script communicates something: who was writing, how quickly, how carefully, and to what end. That information does not live in the words, because there are no words. It lives entirely in the way the marks sit on the page - and it survives the complete absence of content.

I built seven presets to explore this.

Even lines, upright characters, moderate spacing
Standard writing. Nothing pushed to an extreme - the baseline the other six are deviations from.
Tighter, slanted lines with several passages struck through
Notetaking. Tighter and faster, lines slanting and starting unevenly, with whole phrases crossed out - the marks of someone writing to keep up with a thought rather than to be read.
Wide line spacing, generous margins, very regular characters
Book page. Wide leading, generous margins, minimal variation - the regularity you only get when something has been set rather than written.
Short centered-feeling lines with large gaps between them
Poetry or calligraphy. Short lines broken well before the margin, a lot of empty page around them - the layout signals that the arrangement itself is meant to carry weight.
A single narrow column of short one- or two-word lines
Shopping list. One short entry per line, almost every line a new line, no paragraphs - a form you read as a list before you read it as language.
Large, wobbly, wildly uneven characters on widely spaced lines
Child’s journal. Big characters, huge size variation, unsteady baselines - a hand that has not yet been drilled into uniformity.
The page nearly filled edge to edge with dense overlapping marks
Deranged scribbles. Everything turned up at once, the page packed past the point of plausibility.

The struck-through passages in several of these are their own small feature: a scribble routine that draws over a run of words, ending with a controllable probability at each word boundary, plus an underline variant. Crossing something out is a gesture with no linguistic content whatsoever, and it is one of the strongest signals of thought and intent while writing.

Plotted notetaking and child's journal styles side by sidePlotted shopping list and professional essay styles side by side

The same styles plotted in ink. From the PurpleMind video.

The PurpleMind Collaboration

November 2024

I made a video about the project with the educational YouTube channel PurpleMind, published at the end of November 2024. Most of the diagrams in this write-up are frames from it.

The video is documentation rather than part of the piece, but making it forced two things that were useful to the work itself.

A second renderer

The animations were built in Manim, which draws filled polygons rather than the single-thickness paths a pen plotter needs. Rather than rebuild the script in another tool - which would have meant maintaining two versions that drift apart, and animating something subtly different from what actually plots - I added a drawQuads flag to the generator.

With the flag off, every stroke is emitted as a plottable line. With it on, the identical generation logic emits Manim-compatible quads instead. Same seeds, same alphabet, same page: one output goes to the Axidraw, the other into the video.

Explaining it forced me to understand it

Writing the script for a general audience meant every arbitrary choice had to be justified out loud, and several turned out not to be justifiable. The alphabet-size and word-length figures were researched jointly for the video, and the reasoning about phonographic versus logographic scripts came out of those conversations. Preparing the project to be read by strangers is also what produced the presets, the README, and a more organized repository - the version anyone can actually download and run.

A Venn diagram of computation and art with this project in the overlap

Animation from the PurpleMind video.

The closing idea of the video is the one I would most want to keep. Complicated generative results do not require complicated means. It is very tempting in this day and age to relegate the modeling of complex systems to powerful AI systems, but often simple rules underlie complex results.

The Gibberish Generator - Asemic Writing, made with PurpleMind

The full video, made with PurpleMind and sponsored by Brilliant.

Exhibition

April 2025 and April 2026

Scribbler was first exhibited at The Frame Gallery in Pittsburgh in April 2025:

Scribbler installed on white plinths at The Frame Gallery

The Frame Gallery, Pittsburgh, April 2025.

Scribbler running at The Frame Gallery

Scribbler at The Frame Gallery.

Standing the machine up

For the second showing, at the Harvard Coop in Cambridge in April 2026, I rebuilt how the machine is presented.

A pen plotter lying flat on a table is hard to watch. You have to stand over it and look down, only one person can do that at a time, and from any normal viewing angle the page is a foreshortened sliver. It also reads as equipment - as a demonstration of a device rather than as a piece.

So I built a stand that holds the plotter and its paper almost vertical, like a page on an easel. The writing faces the room, several people can watch at once, and the machine reads as an object on display rather than a gadget on a bench. The measurements below are the start of that build; the CAD file for the plotter mount is in the project repository.

The plotter on a wooden board with measurements marked outLayout measurements for the near-vertical stand

Measuring for the stand.

The finished near-vertical stand holding the Axidraw and its paper

The finished near-vertical mount.

Scribbler installed and running at the Harvard Coop

The Harvard Coop, Cambridge, April 2026.

Scribbler running at the Harvard Coop

Scribbler at the Harvard Coop.

The generator is still a live tool rather than a finished artifact. It writes to SVG, so it drives any plotter or any software that reads vector paths, and its script keeps drifting as I change the numbers - which is, in a small way, what written languages do. The code is on GitHub if you want to generate a page of your own.