Skip to content

convictional/souls-only

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

104 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Souls Only: a human-readable font and human-hearable audio that is intended for humans, not AI

Can one create a written or spoken communication that is easily accessible by the general public, but not accessible by AI? The answer is most likely "certainly not!" but this project is my quirky attempt at it.

Why and what is this (and what it isn't)

This project is a heavily vibe coded, conceptual companion to an essay I wrote (without AI assistance, you can check the recording!) called "Souls Only". AI is a tremendously powerful and useful tool and this project shouldn't imply some general anti-AI point of view. However, it explores a few quirky possibilities for creating communication that sighted and hearing humans can perceive that throws typical AI agents perceptual curveballs.

It is worth saying plainly that Claude Code wrote most of this: the font build, the audio toolchain, the demos, and much of this README below this section, with me directing it. The irony that a project exploring a way to create human-exclusive communication (sighted and hearing people) was heavily implemented by claude code is not lost on me, and if anything sharpens the point. The AI tools are now good enough to make this kind of thing easy for a person who's never built something similar before. But that is actually the point of this exploration. The distinction between times where AI intermediation is useful and appropriate (helping me, a guy who don't really know font or audio technology prototype an idea), and instances where an author wants their readers/listeners to be other people. AI has tremendous applications, but perhaps there are instances when there is value sought by the writer/speaker and the reader/listener in knowing that AI intermediation is likely not happening.

The ultimate way to guarantee no intermediation is to use modern encryption techniques and to ensure your readers/listeners have the key to decode. But it becomes more challenging when an author wants the communication to be easily perceivable by the human public and challenging or cost prohibitive for AI.

I believe there will be eventual technologies that support communicating human provenance of work, but it seeems impossible, given the current and assumed rate of AI improvement to create something for human general public consumption and block out AI perception. This may be okay. I fully expect the techniques used in this project to be laughably obsolete soon.

We generally want to create content that is accessible to as many people as possible, across languages and abilities and accessibility optimizing benefits AI perception of the communication. Accessibility technology, including tools leveraging AI have benefitted the goal of accessibliity. For that reason, I would't recommend leveraging the fonts included here for website content intended for the general public. What attempts to break AI perception, will certainly do similar to accessibility screen reading tools unsighted users rely on so please consider this project more in the conceptual, experimental, creative realm and far from the practical.

Soul signing off, Bill


The fonts

SoulsOnly.ttf - A font whose rendered glyphs spell readable text while the stored character stream (what copy-paste, HTML/PDF extraction, and scrapers see) is noise. The font is the decoder, applied only at the rendering layer, and the cipher is driven by an ordinary keyboard with special firmware: you type normal keys, the keyboard emits the noise stream, and only this font renders it back into words.

SoulsOnly-VF.ttf - Renders unreadable glyphes by default and requires a user to "focus" the font as well as dinstinguish between legible, but non semantic decoys. This is an attempt to combat AI screen shot capture and optical character recognition. Note that anything a human user can do, a sufficiently powerful AI agent can replicate, so this is only throwing curveballs that are mean to be easy for human users to "hit" and harder for AI.

This is a craft and statement project, not a claim of unbreakable security. See Limitations in font-cipher-brief.md.

The REVL dial loops between the true text and a decoy on each side: the same line reads as different real-but-wrong words at the trap settings, and as the truth only at one hidden point

Try it (interactive demo)

The repo ships an interactive page, demo/decoy.html, where you can drive the whole thing yourself:

The interactive decoy demo: a typed message renders as readable text above, while the stored byte stream below is noise; a dial and decoy buttons scrub the REVL axis

Type any message, then turn the dial (the slider, or the D1-D6 buttons that jump to decoy focal points). At most settings the line reads as a real-but-wrong message; the true text appears only at one spot the buttons don't mark. The lower pane shows the stored bytes (what a scraper or LLM sees), which stay noise at every setting. Select and copy the rendered text to confirm the copied bytes never contain the real words.

Build the assets and open it locally:

./.venv/bin/python -m fontbuild.build_keyboard    # dist/SoulsOnly.ttf + SoulsOnly-VF.ttf
./.venv/bin/python tools/make_demo_assets.py      # demo/cipher_table.js + demo/SoulsOnly-VF.ttf
python -m http.server 8753 --directory demo       # then open http://localhost:8753/decoy.html

How it works

A font has two streams people usually conflate: the character stream (stored bytes) and the glyph stream (what is drawn after cmap and GSUB run). This project decouples them:

  • The font maps each ASCII code carrier in cmap to a glyph carrying a meaningless half-glyph fragment (so stray, un-ligated text renders as noise, not blanks), then a GSUB liga rule collapses each 2-character code into one opaque half-glyph.
  • The two half-glyphs tile into the real character. Shared classes reuse one canonical left half so the left image is ambiguous: the lowercase bowl a c d e g o q, the lowercase stem m n r u, and the uppercase bowl O C G Q each share a single left half-glyph. Half-glyph names are opaque, so a font-table dump reveals only meaningless half-shapes, never a half-to-character mapping.

Because four ASCII characters collapse into one rendered character, the stored byte count and the rendered glyph count deliberately diverge.

Plain letters typed in the font do NOT decode: letters are themselves carrier glyphs and carry meaningless half-glyph fragments, so pasting ordinary text and applying the font yields noise. Readable words only ever come from the cipher stream, which reinforces that the font is the key, not a normal typeface.

This is the SoulsOnly.ttf font.

The reveal (REVL axis)

The second font SoulsOnly-VF.ttf" is a variable font with a custom REVL axis. Turning the dial does not simply scatter and unscatter the text; it lands on a series of focal points, and at each one every glyph snaps into a real character. At most of them the characters are the wrong ones: the line reads as a plausible but false lowercase message (a decoy), so anything that scrubs the axis and runs OCR comes away with non-semantic garble. Between focal points the glyph vertices travel, so the letterforms smear from one into the next.

The true text appears at exactly one focal point. Crucially, nothing is stored at any focal point within the font file (not the decoys and not the truth). Each one materializes only by interpolation between two flanking garbage masters whose random swings cancel at the focal's center. This is intended to make reverse engineering harder. So:

  • every master in the file is noise; dumping them reveals neither a decoy nor the real text,
  • the real point is structurally identical to a decoy: you cannot tell which focal is real by inspecting the font, only by knowing its dial value,
  • the distortion is symmetric across the whole axis (no telltale extra churn marking where the secret lives).

This is also what makes the font harder to reverse-engineer than an ordinary variable font. The usual shortcut is to read the masters straight out of the file, or to ask the font for a named instance, and recover the design from the stored data. Here there is nothing in the data to recover: no master, no named instance, and no single stored axis value holds readable text. The readable letterforms exist only as transient shapes the renderer interpolates on the fly between two garbage masters, and the position of the real one is a number kept out of the file entirely. That pushes an attacker off the cheap path of inspecting the file and onto the expensive one of driving the renderer across the axis and reading pixels, which is the honest limit described next.

The entire decode and reveal mechanism lives in the font (cmap, GSUB, half-glyph tiling, and fvar/gvar); a page contributes only the single REVL axis value via one control. The axis is unnamed and there is no legible named instance, so the reveal value is not handed to an automated reader for free.

Honest limit (restated from the spec): the REVL value is one bounded number, so an automated attacker can sweep axis values and OCR every focal point. The decoys mean that sweep yields several equally-plausible readings with no way to rank them, but a reader who knows the dial value, or recognizes the real message, still wins. This layer is the most portable and self-contained reveal, and the weakest against automated vision. It is a statement device, scoped as such.

Install the fonts

The built fonts are committed in dist/:

  • dist/SoulsOnly.ttf: the static font. Renders a cipher stream as readable text.
  • dist/SoulsOnly.otf: the same static font with CFF (PostScript) outlines, for tools that prefer OTF.
  • dist/SoulsOnly-VF.ttf: the variable font (family "Souls Only VF") with the REVL decoy axis. Defaults to noise; most of the dial shows decoys (real but wrong words) and the true text appears only at one hidden point, REVL = 650 (see "The reveal" below). TTF only: the axis lives in TrueType variation data, which is also the most widely supported variable-font format.

Download and double-click to install (Font Book on macOS, right-click → Install on Windows), or use @font-face on the web. Remember the font only decodes the cipher stream; ordinary text rendered in Souls Only is noise by design. Generate a stream with the encoder below or the cipher keyboard firmware. The fonts are licensed under the OFL 1.1 (see Licensing).

Flashing the keyboard

A Keychron V1 Max, the QMK keyboard this build targets for the Souls Only cipher firmware

The keyboard half runs on any QMK/VIA board (this build targets a Keychron V1 Max). Its firmware is generated from the same source as the font, so the codes can never drift: tools/make_qmk_table.py writes demo/qmk/cipher_table.h, which drops into a QMK keymap next to demo/qmk/keymap_cipher.c. Once flashed, Right Ctrl toggles cipher mode on and off and recolors the backlight as the on-state indicator: Souls Only Blue while ciphering, plain white when off. Off, it is an ordinary keyboard, and space, Enter, Backspace, and the arrows all behave normally.

The full hardware runbook (toolchain, keymap wiring, entering DFU, recovery) is in demo/BUILD.md. It is genuinely fiddly the first time, so the easiest path is to let Claude Code do it: open this repo in Claude Code, plug the board in, and ask it to flash the cipher firmware. That is exactly how this release was flashed. Claude regenerated the table, compiled against the Keychron QMK fork, waited for the board in DFU, and ran dfu-util end to end. Reflash whenever the cipher changes, since a board flashed for an older build emits codes the current font no longer decodes.

Charset and editing

Souls Only covers the full US-QWERTY printable set: lowercase, uppercase, digits 0123456789, and the standard symbols. Whitespace stays editable in whole characters: every character is four bytes, so the keyboard deletes and navigates in units of four (Backspace removes four, the arrows move four), Space emits one space character, and Return emits a real newline plus three invisible pad bytes so the stream stays four-aligned.

Layout

docs/superpowers/             design specs and implementation plans
cipher/charset.py             the charset + half-slot model: single source of truth
cipher/keyboard.py            ASCII carrier-code allocation + encode/decode oracle
cipher/decoy.py               per-focal substitution mappings (the decoy letters)
cipher/qwerty.py              US-QWERTY keycode -> character map
fontbuild/fragments.py        half-glyph slicing (skia-pathops)
fontbuild/resample.py         uniform outline resampling so any half can morph
fontbuild/features.py         GSUB liga compilation from a FEA file
fontbuild/build_keyboard.py   build dist/SoulsOnly.ttf (+ the REVL variable font)
fontbuild/decoy_reveal.py     build the decoy-focal REVL font (true text not stored)
tools/make_qmk_table.py       generate the QMK firmware table from cipher/keyboard
tools/make_demo_assets.py     generate the browser demo table + copy the VF
tools/make_keys_preview.py    generate dist/keys.html (the REVL slider preview)
demo/                         the physical cipher keyboard demo (QMK keymap, runbook, page)
tests/                        pytest suite (run via python -m pytest)
base/Jost-Regular.ttf         instanced OFL base font (glyph outlines)
audio/                        the audio sibling: a phase-scramble reveal of a spoken message (own README)

(dist/ and the generated demo assets are build artifacts and are gitignored.)

Setup and run

python3 -m venv .venv
./.venv/bin/pip install -r requirements.txt
bash scripts/fetch_base_font.sh        # if base/Jost-Regular.ttf is missing

./.venv/bin/python -m fontbuild.build_keyboard   # dist/SoulsOnly.ttf + SoulsOnly-VF.ttf
./.venv/bin/python tools/make_otf.py             # dist/SoulsOnly.otf (CFF outlines)
./.venv/bin/python -m pytest                     # run the suite
./.venv/bin/python tools/make_keys_preview.py    # dist/keys.html (the REVL slider)
# then: python -m http.server 8753  and open dist/keys.html

# regenerate the physical keyboard demo assets:
./.venv/bin/python tools/make_qmk_table.py       # demo/qmk/cipher_table.h
./.venv/bin/python tools/make_demo_assets.py     # demo/cipher_table.js + demo/SoulsOnly-VF.ttf
# then open demo/index.html  (see demo/BUILD.md for the hardware runbook)

Encode and decode by hand

./.venv/bin/python -c "from cipher import keyboard as k; print(k.encode('hello world'))"
./.venv/bin/python -c "from cipher import keyboard as k; print(k.decode(k.encode('hello world')))"

An Audio Version of Souls Only

audio/ is a parallel artwork: the same reveal, in sound, read with your ears instead of your eyes. A spoken message ships already scrambled into noise and reassembles into an intelligible voice only at a hidden point on the same 0 to 1000 dial the font's REVL axis uses.

The audio demo: a tuning dial over an oscilloscope of static, above an animated build lane that runs from clean speech, through a phase scramble, to three channels layered into a single clip, to the message rebuilt at its hidden focal

Where the font warps glyph outlines along REVL, the audio phase-scrambles the recording: an FFT keeps each frequency's amplitude but a seeded mask randomizes its phase, so the voice is genuinely destroyed rather than buried under added static. Rotating the phase back by the dial amount reconstructs it, but only where that amount matches the one baked into the asset offline.

The method is a code-division overlap. Three channels - the spoken message and two nursery-rhyme decoys (Twinkle Twinkle and Old MacDonald) - are each phase-scrambled at their own focal dial value and summed into one clip the length of a single recording. Tuning to a channel's focal rebuilds that voice while the other two stay a wash of noise; the short decoys loop so every channel plays the whole way through. Turning the dial is like a radio: noise everywhere, one voice surfacing as you pass its focal.

That overlap is also what hides the message from machines. A speech-to-text model run at any focal hears the target voice buried under the other two and transcribes only gibberish, so no adversarial trick is needed - the two-voice floor masks the words on its own. The one thing that recovers the message is a human primed by the words on screen, picking the voice they were set to hear out of the murmur. It is the same human-perception gap the font leans on (top-down priming, you hear what you are set to hear), turned into the defense.

The reveal is pushed one step further than the font's. The font's REVL value is a number in the shipped variable font; the audio's focal value is not in the shipped code at all; it lives only in the offline build tool, so reading the source does not hand it over. The honest limits: a single bounded dial can still be swept, and the masking is not airtight - a larger transcriber, or a patient human who listens across the dial, can still pull the message out (Whisper-tiny gets only gibberish, but a few words can leak). It raises the cost of an automated attack and turns the task back into listening; it is not an unbreakable cipher. A statement device, scoped as such.

See audio/README.md to run and build it.

Elsewhere

I work and build at Convictional, and write essays at Philosophy of Work about how work is changing in the age of AI. This project is a small artifact of that same preoccupation.

Licensing

Dual-licensed:

  • Code (cipher, fontbuild, tools, firmware glue): MIT.
  • Font files: glyph outlines come from Jost, Copyright 2020 The Jost Project Authors, licensed under the SIL Open Font License 1.1. The committed base/Jost-Regular.ttf (instanced) and any built SoulsOnly*.ttf are derivative Font Software and are distributed under the same OFL 1.1; they are not MIT.

About

A quirky exploration of how written or audio content could be made accessible to humans and inaccessible to AI bots.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors