Format specification · version 2

ShapeAddress format

Status: draft for v0.3, 2026-10-19. This page is the HTML form of spec/FORMAT.md in the repository; the Markdown is canonical. Normative words: must, should.

1. Model

An input is a byte string of length 1 or more. A dictionary is an ordered list of n colours and an ordered list of m glyphs with n x m = 256, identified by a lowercase id matching [a-z0-9][a-z0-9-]*. Each byte b maps to one cell:

shape  = b mod m          (index into glyphs)
colour = (b - shape) / m  (index into colours)
b      = colour * m + shape

For the default dictionary sa330 (n = 8, m = 32) this is colour = b >> 5, shape = b & 31.

Inputs: hex strings (optional 0x, case-insensitive, even length), UTF-8 text, or raw bytes. Wallet addresses on EVM networks are 20 bytes and render as 20 cells. Checksum casing (EIP-55) never changes the picture.

0x00: colour 0, glyph 0
0x1f: colour 0, glyph 31
0x20: colour 1, glyph 0
0xff: colour 7, glyph 31

2. Layouts

grid, 20 cells, 4 rows
coin, ring of 20 plus checksum

3. SVG carrier

Every rendered SVG root carries data-dict="<id>" and data-sa="2:<id>:<layout>:<hex>". Each cell is a <g data-i="<index>" data-shape="<glyph name>" data-color="<colour name>">; the checksum cell has data-i="-1". A decoder must recover the bytes from the cells' names using the dictionary named on the root and must not rely on the data-sa hex, which is informative.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="64" height="64" aria-hidden="true"
     data-dict="sa330" data-sa-input="2:sa330:grid:00">
  <g transform="translate(0 0)">
    <g data-i="0" data-shape="circle" data-color="red" fill="#fff">…</g>
  </g>
</svg>

4. Spoken form

The legend of a cell is <colour name> <glyph name>, read in cell order. Names are the dictionary's; sa330 byte 0x00 is "red circle", 0xff is "pink lens".

5. Options that do not change the data

size, style (tile or glyph), cell shape, gap, radius, background, palette (cvd hatching), labels, title, highlight. Changing any of them changes the picture but not the decoded bytes.

6. Dictionaries

A dictionary id names an exact glyph and colour list forever. Changing a glyph or colour requires a new id. Registered ids in this repository: sa330 (32 owned glyphs, 8 hues, MIT), fa330 (the original lab set, Font Awesome Free glyphs, CC BY 4.0). Third parties may register their own; ids starting with sa are reserved for ShapeAddress. The object shape and the registry rules are on the dictionaries page.

7. Test vectors

spec/test-vectors.json lists inputs, options, expected hex, legend and the SHA-256 of the SVG string produced by the reference implementation. A port is conformant when it reproduces the hex and legend for every vector and decodes the reference SVGs; byte-identical SVG is required only for implementations that claim to be the reference renderer.

{
  "name": "sa330 grid default, vitalik",
  "input": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
  "options": {},
  "hex": "d8da6bf26964af9d7eed9e03e53415d37aa96045",
  "legend": ["purple flower", "purple three-dots", "green moon", "pink bar", …],
  "sha256": "693fedecf192eb2cff269cd50eddf4c1e065b5c16a5753a98d79991e2c379f40",
  "bytes": 6055
}

The vectors cover both dictionaries, both layouts, text input, all 256 bytes in one picture, and the single bytes 0x00 and 0xff. They are regenerated with npm run vectors after any intentional change to a dictionary or the renderer, and that change is a breaking one.