Skip to content

The format

The format

Document structure

A 3md document is UTF-8 text with three regions, in order:

  1. A required frontmatter block.
  2. An optional preamble of Markdown (any content before the first plane).
  3. Zero or more planes.
---
3md: 1.0
axis: time
title: My Week
---
Optional preamble Markdown.

@plane z=0 label="Monday"
# Monday
- [ ] Standup

@plane z=1 label="Tuesday"
# Tuesday

A leading UTF-8 byte order mark (BOM) is ignored, and blank lines before the opening --- are allowed.

Frontmatter

The document MUST begin with a frontmatter block: a line containing exactly ---, one or more key: value lines, and a closing line containing exactly ---.

This is not YAML. It is a small, flat, line-based mini-format. Do not feed it to a YAML parser. The rules:

  • Each line is one key: value pair, split on the first colon. A line with no colon is invalid frontmatter.
  • Keys and values are trimmed of surrounding whitespace.
  • Blank lines and lines whose first non-whitespace character is # are ignored (a # line is a comment).
  • A value may be wrapped in matching single or double quotes, which are then stripped. Inside a quoted value, \\ becomes a backslash and \" becomes a double quote. Unquoted values are taken verbatim.
  • Duplicate keys are last-wins.
  • There is no nesting, no lists, no typed scalars, and no multi-line values.

Reserved keys

These three keys are matched case-insensitively and have defined meaning:

KeyRequiredDescription
3mdYesThe format version string. Its presence is the magic marker. The value is recorded but never validated, so older markers like 0.1 stay valid.
axisNoThe meaning of the Z axis. Trimmed and lowercased. Any string is permitted. Defaults to layer.
titleNoA human-readable title.

Every other key is preserved verbatim as string metadata and never coerced to a number or boolean.

Conventional metadata keys

These are ordinary string metadata to the parser (they never affect parsing), but renderers MAY honor them:

KeyDescription
viewA preferred default view a renderer opens in. Canonical values: stack, play, single, present, blend, map. Older names layers, parallax, elevator, and scene are accepted as aliases. See the viewer docs.
legendA per-document character map applied only inside fenced blocks, given as whitespace- or comma-separated char=replacement pairs, e.g. legend: g=🟩 w=🟦 .=·.
billboardWhen true (also yes or 1), the viewer makes glyphs face the camera in 3D object views. Used by voxel and animation examples.

You may add any other keys you like (author, category, and so on); they are kept as metadata.

The @plane directive

A plane begins with a directive line whose first token is @plane, followed by space-separated key=value attributes:

@plane z=2 label="The Armory" x=1 y=-1

Rules that keep plane boundaries unambiguous:

  • The directive MUST begin at column 0.
  • It MUST lie outside a fenced code block. A @plane line inside a ``` or ~~~ fence, or indented as code, is body text, not a new plane.
  • Every attribute token MUST contain an = (split on the first =); attribute keys are lowercased.
  • Everything after the directive, up to the next @plane or end of file, is the plane's Markdown body. Leading and trailing blank lines of a body are trimmed.

Plane attributes

AttributeRequiredDescription
zYesThe plane's position on the Z axis. A finite decimal: optional sign, digits with optional fraction and exponent (e.g. 0, -2.5, 1e3). Hex, inf, and nan are rejected.
x, yNoFinite decimal in-plane offsets (same grammar as z) for spatial viewers.
labelNoA human-readable name for the plane.

Any other attribute is preserved as a string on the plane. Quote a value if it contains spaces; inside a quoted value, \\ and \" are escape sequences, and an unterminated quote is an error.

Two planes MUST NOT share the same z. Source order is preserved, though viewers may reorder by z.

Single-plane shorthand

If a document has frontmatter but no @plane directives, the entire body is one implicit plane at z = 0.

Fenced-grid bodies

A plane body is plain CommonMark, so a fenced code block renders exactly as written. This is how grids, ASCII frames, and voxel slices are authored. With a legend: key, single source characters inside fences are substituted on render:

---
3md: 1.0
axis: frame
legend: x=⬛ S=🟫 C=🌽 .=🟩
---
@plane z=0 label="Spring"

x x x x x x S C C x x . . . x

Without a legend, renderers MUST NOT substitute characters on their own.

A plane body may reference another plane by its z position with a double-bracket link:

See [[z=2]] for the details, or jump [[z=0|back to the start]].

The grammar is [[z= followed by a finite decimal, an optional | and link text, then ]]. If the captured target is not a finite decimal, the sequence is not a link and stays literal text.

Links live inside the Markdown body verbatim; implementations expose them through a separate extraction step that reports the source z, target z, optional text, and whether a plane with that target exists. This makes link validation portable: the CLI links and check-links commands use it to find dangling references.

Errors

A conforming parser rejects:

ErrorCause
missingFrontmatterNo frontmatter block.
invalidFrontmatterA frontmatter block that is never closed.
missingVersionNo 3md version key.
missingPlanePositionA @plane directive with no z.
invalidPlaneDirectiveA non-finite z/x/y, an attribute token with no =, or an unterminated quote.
duplicatePlaneTwo planes with the same z.

For the full grammar and conformance rules, see the spec.