A new visual language for talking to LLMs — and for watching them think back.
This repo contains two complementary systems that share one premise: every mark on the figure must correspond to a real, measured property of the input. No randomness. No styling for effect. The magic-circle aesthetic emerges from honest projection.
Both systems run locally on CPU. No API keys, no telemetry, no GPU required.
Renders a markdown prompt as a deterministic sigil. The webapp also runs a small LLM in-browser via transformers.js, projects its output back onto the figure as a thought trajectory, and exposes a tensor scope that visualises every layer's hidden state as a mandala.
| 1. blank | 2. place glyphs | 3. bridge | 4. conjure |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
| empty canvas | click ◉ △ ⊞ ▽ ✦ to place typed sections |
click two vertices to bridge | scaffold + conjure → real sigil |
Three modes of human ⇄ AI exchange:
- Human → AI: place typed glyphs on a circle (persona, reasoning, tools, limits, …) instead of writing prose. The geometry encodes intent; the user types almost nothing.
- AI → Human (output): as the model generates, its output is projected into the prompt's semantic space and drawn as a trail across the figure. You can see which parts of the prompt the model is drawing from at each moment.
- AI → Human (interior): open the tensor scope to peer inside the model itself. The hidden state of every token at every layer becomes a petal of a layered mandala, and a logit-lens decoder shows what word the model "thinks" each token is at each depth.
A prompt can also be reduced to a minimal sigil-equivalent form: the compressor keeps the contract-bearing parts (headings, code, imperatives, URLs) verbatim and ranks remaining prose by importance, so two prompts that produce similar sigils now also produce similar markdown.
pip install -r requirements.txt
python webapp/server.py
# → open http://localhost:8765
# CLI (static SVG)
python -m prompt_sigil examples/complex.md
# → out/complex.svg
# CLI (compress; protected content survives any ratio)
python -m prompt_sigil compress examples/complex.md --ratio 0.4 --render-pair
# tokens 1040 -> 493 (47.4%)
# preserved 23 imperatives, 5 code blocks, 2 urls
# also writes before/after sigils for visual diffThe webapp runs entirely on your machine. The optional in-browser LLM
(SmolLM2 / Qwen3 / Gemma 3 / Gemma 4) is downloaded once into IndexedDB
and runs locally afterward — no API keys, no telemetry. The tensor
scope additionally needs torch + transformers (CPU-only is fine)
and optionally umap-learn.
A PyTorch backend (Qwen2.5-0.5B, 24 layers, 896-dim hidden state) extracts the middle layer's residual stream for any prompt, projects it through a fixed PCA-2 basis built from a 210-phrase bilingual reference corpus, and renders the result as a sigil.
The same figure works in reverse: draw a point on the circle, get text that steers in that semantic direction.
| Encode (text → figure) | Decode (figure → text) |
|---|---|
| Sentence → forward pass on Qwen2.5-0.5B → mean-pool layer 12 → L2-normalize → project through fixed PCA-2 basis → 2D position + per-token trajectory | User clicks a point → K-nearest atlas phrases in 2D → average their full 896-d hidden states → register forward_hook on layer 12 that adds α·v to residual → sample K completions |
The atlas is laid out as 17 category arcs around the rim (instruction, nature, polite, tech, …). Each category has a Unicode glyph mnemonic; phrases within a category form a coloured band. The pool of a new encode lands inside the ring, with per-token hidden states drawn as a numbered trail.
# uv-managed environment (recommended)
uv sync
# 1. One-time: build the fixed PCA-2 basis from the reference corpus.
# Downloads Qwen2.5-0.5B (~500 MB) on first run, then ~45 s to encode
# the 210-phrase corpus on CPU.
uv run python -m latent_sigil.build_basis
# 2. Run the FastAPI server
uv run uvicorn latent_sigil.server:app --port 8766
# → http://localhost:8766/latent.htmlGeneration costs ~4 s per 40-token sample on CPU. The encode endpoint returns in ~0.2 s for a sentence.
- Click ⌂ blank to start with an empty canvas.
- Pick a glyph: ◉ persona, △ reasoning, ⊞ tools, ◇ output, ✦ style, ▽ limits, ○ memory, ⌬ examples.
- Click on the figure to place a typed vertex (it snaps to the rim).
- Click an existing vertex of the same type to cycle its preset.
- Use ↔ bridge to connect two vertices semantically.
- Click ✦ scaffold — the markdown system prompt is generated for you.
- Click conjure to render the real sigil from that prompt.
- Open live inference, load a model (SmolLM2-135M is the fastest first download), type a question, click infer.
- Watch the gold thought trajectory draw itself across the circle.
You can also free-hand draw on top of an existing sigil:
- Closed shape around a section → emphasize it in the next query.
- Line through ≥2 sections → bridge them.
- Two crossing strokes near a section → suppress it.
The drawn intent is appended to your query before inference.
Open the ⌗ tensor scope panel and click extract: the server runs
one forward pass with output_hidden_states=True (PyTorch + HF
transformers, bypassing the ONNX-export limitation that would otherwise
drop intermediate layers), projects all (layer × token) hidden states
through a single shared PCA / UMAP / t-SNE basis, and renders one of
two views.
Mandala view is the magic-circle reading. Concentric rings = layers (outer = embedding, inner = output). Each token sits at a fixed angle around the rim; following its radial spoke shows how its representation travels through depth. Per-layer normalisation keeps the colours vibrant even when one PCA axis dominates the global variance.
Scatter view is a 3D point cloud through PCA space, with each token's trajectory as a polyline.
In either view, every cell carries its own meaning:
- Hover: tooltip with token, layer, the three PC values, and the top-k logit-lens predictions for that cell (nostalgebraist 2020).
- Click a token to follow its spoke through every layer; its full D-dim hidden state is drawn as a morphing radial shape that reshapes between layers, synced with a top-k bar chart.
- Click a layer ring to jump to that layer.
- Scroll = zoom (cursor-anchored), drag = pan, double-click = reset.
The tensor scope is intentionally decoupled from compression. It is a viewer for human inspection, not a pipeline stage; nothing flows back into prompt processing.
- Type a sentence into ① 文字 → 魔法陣 and click Encode. A gold disc appears at the pool position; the per-token trajectory threads through it numbered 1..N with a blue→gold gradient.
- Click anywhere inside the ring to place a green draw mark. The mark's angle (relative to centre) determines which category direction the steering will pull toward.
- Set a prompt under ② 魔法陣 → 文字 (the model continues this text) and click Generate. Three samples appear, all sharing the nuance of the direction you drew but with different surface text.
- The view supports mouse-wheel zoom, drag-pan, and
double-click to reset.
⤺ atlassnaps back to atlas-only zoom;⤡ fit allframes everything including any encoded trajectory. ◯ ring/✦ scattertoggles between the magic-circle ring layout and the raw PCA-2 scatter (same data, different view).
The steering sweet spot is α = 5–10. Below 3 the nuance is invisible; above 20 the model collapses into noise tokens.
Every visual element is a 1:1 mapping to a measured property of the input. Examples:
Prompt Sigil:
- Section vertex angle = atan2 of the section's PCA-2 embedding.
- Slot width = equal (sections are parallel things, not weighted by tokens).
- Tick band density = token count.
- Inner polygon edges = pairwise cosine similarity between sections.
- Sentence cloud = each sentence as a 2D point in the same PCA basis.
- Thought dots (gold) = generated text projected through the same basis.
Tensor scope:
- Ring index = layer (outer → embedding, inner → output).
- Angular position = token order.
- Cell colour = PCA / UMAP / t-SNE projection of the cell's hidden state.
- Morphing radial shape = the selected token's full D-dim hidden state.
Latent Sigil:
- Gold pool disc = full prompt's layer-12 hidden state, projected.
- Numbered token dots (blue→gold) = each token's hidden state at layer 12, with the colour gradient encoding sequence position.
- Atlas glyph position on the rim = sort order of category centroid angles.
- Atlas phrase position within a slot = its individual angle in the slot.
- Green draw mark = user's chosen steering direction.
Same input always produces the same sigil. There is no randomness, no "styling for effect."
prompt_sigil/ Pure-Python lib: markdown → measured tree → 2D projection
parse.py Hand-rolled markdown parser (Japanese imperatives included)
analyze.py TF-IDF + PCA-2 (CLI), embedding-based PCA-2 (webapp)
render.py SVG output for the CLI path
compress.py Importance-scored extractive compression
latent_sigil/ Qwen2.5-0.5B mid-layer pipeline + FastAPI server
__init__.py Constants: MODEL_ID, LAYER=12, D_MODEL=896
model.py Lazy model load, encode_text(), generate_with_steering()
corpus.py 210 bilingual reference phrases across 17 categories
build_basis.py One-shot: encode corpus, fit PCA-2, save atlas + basis
server.py FastAPI: /api/atlas, /api/encode, /api/decode
cache/ basis.npz + atlas.json (gitignored, built locally)
webapp/ Two frontends, one stdlib server
server.py Stdlib HTTP server: /api/{layout, project, parse,
positions, compress, pca, hidden_states} on port 8765.
HF transformers loaded only by /api/hidden_states —
keeps the base demo torch-free.
static/index.html Prompt Sigil UI + tensor scope (single-file, no build)
static/latent.html Latent Sigil UI (single-file, talks to FastAPI on 8766)
examples/ Sample prompts for prompt-sigil
docs/ Screenshots, GIF, わかる.md (middle-school explainer)
pyproject.toml uv-managed Python project (torch, transformers, fastapi)
requirements.txt pip-friendly equivalent for the prompt-sigil server
The original Prompt Sigil server stays in stdlib (http.server) — its
job is to serve static files and run a small TF-IDF + PCA pipeline. The
Latent Sigil server needs to host Qwen2.5-0.5B in process, which
benefits from async (FastAPI/uvicorn) and a structured JSON API. They
run on different ports (8765 and 8766) so you can run either, or both,
without conflict.
- Python 3.10+ for the Prompt Sigil base demo
- Python 3.12+ for Latent Sigil (modern type hints)
- CPU-only PyTorch is sufficient — no GPU needed
- ~700 MB disk for the Qwen2.5-0.5B model cache (Latent Sigil)
- ~22 MB disk for the in-browser embedder (Prompt Sigil) — cached in IndexedDB by transformers.js
The pyproject.toml pins the PyTorch CPU wheel index so uv sync does
the right thing on Windows/macOS/Linux without manual flags.
Working today (May 2026):
- Deterministic markdown-prompt → magic-circle rendering (Prompt Sigil)
- 8-glyph composer for click-only prompt building
- In-browser inference with thought-trajectory projection
- CSS-driven rotation that survives WASM main-thread blockage
- Free-hand stroke interpretation (emphasize / suppress / bridge)
- One-click prompt scaffolding from typed-glyph composition
- Extractive prompt compression that preserves sigil semantics
- Tensor scope: per-layer per-token mandala with PCA/UMAP/t-SNE, zoom-pan, hover tooltips, click-to-follow, logit lens
- Selected-token morph: raw D-dim hidden state as a closed radial shape that reshapes between layers, synced with top-k bar chart
- Qwen2.5-0.5B encode + decode endpoints with K-NN-in-2D steering (Latent Sigil)
- Ring atlas with 17 categorical arcs, glyph mnemonics, color bands
- Double-scale rendering (atlas at one scale, encoded marks at another so both stay visible regardless of prompt length)
Tuning knobs:
latent_sigil/__init__.py:LAYER— currently 12 (the geometric middle of Qwen2.5-0.5B's 24 layers). Earlier layers are more lexical, later layers more task-specific.- Default
alpha=7in/api/decode— the steering vector magnitude. See "How to use Latent Sigil" above for the sweet-spot range.
Pick one, open a PR.
-
AI proposes a sigil. User states a goal in one sentence; an LLM proposes a typed-glyph layout the user can accept or edit.
-
Larger steering vocabulary. The current 210-phrase corpus covers 17 categories. Doubling that and adding more linguistic registers (academic / vernacular / poetic / technical) would sharpen the ring.
-
Live thought trajectory during generation. Currently the trail is drawn after generation completes because WASM blocks the main thread. Moving inference into a Web Worker would unblock this for Prompt Sigil.
-
Higher-rank basis for Latent Sigil. PCA-2 captures ~37% of the variance in the normalized hidden states. PCA-3/4 with one extra visual channel (size, opacity) could show more structure without collapsing the figure.
-
Vertex drag. Allow composed vertices to be dragged along the rim so the user can curate the angular layout.
-
Save and share. Encode sigil state into a URL hash so a circle can be sent as a link, loaded, and remixed.
-
Hover preview of atlas phrases. Currently atlas dots have
<title>tooltips. An overlay panel that shows the phrase as the user hovers would be more discoverable. -
Hidden-state-driven compression. Use the tensor scope's activation norms to bias the compression's
token_importancemap. Wiring is already inwebapp/static/index.html.
-
Mechanistic interpretability: BertViz, exBERT, Anthropic's Scaling Monosemanticity, Distill's Activation Atlas, Goodfire's SAE Manifold. Diagnostic and rigorous, rarely beautiful.
-
Latent-space art: Refik Anadol's Unsupervised. Beautiful but the data is images, and the projection isn't designed to be functionally read.
-
Procedural sigil generators: Sigil Engine, Alchemy Circles Generator. Parametric aesthetics, not tied to real data.
-
Human–AI co-creation: SketchAgent (MIT/Stanford CVPR 2025), Real-Time AI Drawing System (arXiv 2025).
-
Activation steering: Anthropic's Golden Gate Claude, Feature Guided Activation Additions, Goodfire Ember. Latent Sigil's decode path is in this family — direction in residual space + sampling for surface variation.
The unique niche of this project: aesthetic + data-driven + input-deterministic + bidirectional. Nothing else covers all four.
The codebase is small (~4,500 lines total) and uses minimal frameworks (stdlib http.server for Prompt Sigil; FastAPI for Latent Sigil; no frontend build step). You should be able to read every file in one sitting.
Non-negotiables:
- Determinism. Same input → same sigil. No randomness in the layout pipeline. (Sampling lives only in the steering decode path, and that is documented as variance-on-purpose.)
- No decorative-only visual elements. Every mark maps to a measured property; "looks magical" is not a justification.
- No build step for the frontend. The single-file
static/*.htmlpattern is load-bearing — drop in a browser and it runs.
If you want to discuss a larger change before coding, open an issue describing the data → visual mapping you propose.
MIT — see LICENSE.
@misc{prompt-sigil-2026,
title = {Prompt Sigil + Latent Sigil:
A Magic-Circle Protocol for Human--AI Communication},
year = {2026},
url = {https://github.com/jackasser/mahojin_magic_AI}
}
An open invitation: a small protocol that anyone can extend. The goal is not a polished product but a shared visual language that grows as more people contribute glyphs, mappings, and ways of listening to what the AI is saying back.






