This specification defines 3a-related concepts, terminology, and how 3a text files should be read.
Content:
- Text
- Concepts
- File Format
- Extending
- Formatting and Optimization
- MIME
- Legacy Format
- Compatibility Notes
The 3a format is Unicode-based; all text-related terms below are interpreted in the context of Unicode.
Definitions:
- "character"/"char" — a Unicode code point.
- "space" / "ASCII space" — the
U+0020code point:. - "newline" —
U+000A(aka\n). - "word" — a sequence of characters delimited by text start/end, newline, or any whitespace character.
- "line" — a sequence of characters delimited by text start/end or newline.
- blank / void / empty line — a line of zero length.
The following characters MUST be ignored by a 3a decoder/encoder:
U+000D(aka\r)- C0 / C1 control characters:
U+0000..U+001F,U+007F..U+009Fexcept newline - Zero-width / joiner characters:
U+200B–U+200F,U+FEFF,U+FE00–U+FE0F - Combining marks:
U+0300..U+036F - Bidirectional control codes:
U+202A..U+202E,U+2066..U+2069 - Surrogate code points:
U+D800..U+DFFF
The following characters MUST be replaced with ASCII space:
U+0009(aka\t, tab)- any character from the Unicode "Space Separator" category
U+180E(Mongolian Vowel Separator)
3a art is a collection of channels.
- Channels are parallel sequences of frames indexed from 0.
- Frames are sequences of rows.
- Rows are sequences of elements of a type that is unique to each channel.
Two primary channels:
- text — holds frames of textual art. Its elements are Unicode grapheme clusters.
- colors — holds colors information (foreground, background) for text-channel elements. Its elements are names of color mappings.
Color mappings bind single-character names to color pairs.
Color pair consists of fg (foreground aka text color) and bg (background) colors. Each one may be:
- ANSI 4-bit colors (Black/Red/Green/Yellow/Blue/Magenta/Cyan/White plus brightness)
- ANSI 256-color codes
- True 24-bit RGB colors
- No color aka default color - depends on environment / software but typically white for fg and black for bg
Predefined color names:
_— default color for both fg and bg.0— ANSI 4-bit black fg (code 30), default bg.1— ANSI 4-bit red fg (code 31), default bg.2— ANSI 4-bit green fg (code 32), default bg.3— ANSI 4-bit yellow fg (code 33), default bg.4— ANSI 4-bit blue fg (code 34), default bg.5— ANSI 4-bit magenta fg (code 35), default bg.6— ANSI 4-bit cyan fg (code 36), default bg.7— ANSI 4-bit white fg (code 37), default bg.8— ANSI 4-bit bright black (gray) fg (code 90), default bg.9— ANSI 4-bit bright red fg (code 91), default bg.a— ANSI 4-bit bright green fg (code 92), default bg.b— ANSI 4-bit bright yellow fg (code 93), default bg.c— ANSI 4-bit bright blue fg (code 94), default bg.d— ANSI 4-bit bright magenta fg (code 95), default bg.e— ANSI 4-bit bright cyan fg (code 96), default bg.f— ANSI 4-bit bright white fg (code 97), default bg.
Colors may be completely disabled; in that case the entire art should be shown using the environment's default fg/bg colors.
If all frames in a channel have identical content, that channel may use a single pinned frame (a frame that applies to all positions). Except when pinned frames are used, text and colors channels MUST have the same number of frames, forming a one-to-one mapping between them.
To control animation timing, there is a delay value that describes the pause between frames in milliseconds. Typically a single global delay is used for the whole art, but frame-specific delays may be specified. A frame-specific delay is applied after the frame is shown. The default global delay is 50 milliseconds.
3a art may also contain metadata such as title, authors, tags, etc.
Some parts of a 3a file are explicitly defined as supporting comments.
When comments are supported, a comment is an entire line that starts with ;;.
There are no block comments and no inline comments; a comment always occupies
the whole line.
A 3a file consists of blocks separated by one or more blank lines. A file must contain at least a header block.
Each block starts with a block title line prefixed with @.
Block titles (after @ prefix) may contain only ASCII alphanumeric characters
and the characters +, -, _, and ..
Block's title is a block's name in lowercase unless otherwise noted.
The header is always the first block and contains art metadata. The header must be always presented even if void. The header supports comments.
Header's title is @3a.
The header is made of key-value pairs, one per line. In a key-value pair, the first word at the start of the line is the key; the remainder of the line is the value. Parsing of the value(s) is key-specific.
Some keys may appear multiple times as explicitly allowed; for other keys, multiple occurrences are an error. Keys and values are case-sensitive unless otherwise noted.
title sets the art title. The whole line after the key is a single string
value. Consecutive spaces SHOULD be collapsed to a single space during
decoding/encoding. Leading and trailing spaces SHOULD be trimmed.
author sets an author. Parsing rules are the same as for title.
The author key may occur multiple times to indicate multiple authors.
Multiple occurrences of author key with identical values (after trimming)
MUST be deduplicated.
orig-author sets the original author (for derivatives). Parsing rules are
the same as for author.
src sets a link to the art's original location (typically a URL).
The whole line after the key is a single string;
leading and trailing spaces SHOULD be trimmed.
editor provides information about the editor software used to create the art.
license defines an SPDX license expression (see https://spdx.org/licenses/)
or the literal string proprietary.
Leading and trailing spaces SHOULD be ignored.
If the license key is not present, or the identifier is unknown,
the license MUST be treated as proprietary unless a different rule applies
in the context where the 3a file is stored
(for example, an explicit LICENSE file in a repository).
delay defines frame delay(s) as described above.
The value may contain a single global delay and/or multiple values separated
by spaces.
- Exactly one value MUST be the global delay: a string representation of an unsigned decimal integer (milliseconds).
- Optionally, any number of frame-specific delays may be provided as colon-separated integer pairs:
<frame>:<delay>. In each pair, the first integer is a frame number (starting from 0) and the second is a delay in milliseconds specific to that frame. Frame-specific delays for non-existent frames SHOULD be ignored.
If delay is not provided, the global delay defaults to 50 milliseconds and
there are no frame-specific delays.
loop controls whether playback loops or stops after the first run.
It has a single case-insensitive boolean value: yes (true) or no (false).
If loop is not provided, looping is enabled by default.
preview sets the frame number to use as a preview. It accepts a single
decimal integer value. The default is 0. If the specified frame does not exist,
the preview key SHOULD be ignored.
col defines a new color mapping. It has one or three values separated by
spaces. First value is a single-character name. Next values can be:
fg:<color>bg:<color>
Colors in fg:<color> and bg:<color> can be:
- kebab-case ANSI color names (
green,bright-red, etc.) - ANSI 3-bit or 8-bit color decimal codes (
16,196, etc.) - case-insensitive RGB hex color codes (e.g.
ff00A0)
There can be only one fg and one bg color in mapping.
If fg or bg color is not provided, default one should be used.
A color mapping name may be defined only once; predefined mappings may be overridden by a user-defined ones.
colors indicates whether colors is enabled for this art (versus plain text).
It has a single case-insensitive boolean value: yes (true) or no (false).
- If
colorsis present, its value controls colors. - If
colorsis not present but at least onecolkey exists, colors is considered enabled. - If
colorsis not present and there are nocolkeys, colors is considered disabled.
Any line starting with # defines tags: every word on that line that begins
with # is a tag. Duplicate tags in the header SHOULD be deduplicated.
There can by multiple tag lines.
@3a
title A Cool Art
orig-author Some other guy
author Me
author You
;; This is another comment
editor nvim
license CC0-1.0
preview 3
loop no
src https://example.com/my-cool-art
delay 10 2:100
#ascii #ansi
#art
colors yes
col + bg:bright-red fg:196
The body is the block that contains the art itself.
It must always be a last block, so it may include blank lines.
The body consists of frames separated by one or more blank lines. Each frame consists of lines separated by newline.
If colors are enabled and text or colors channels are not pinned, each line is represented by two consecutive rows with equal length:
- the first is from the text channel
- the second is the corresponding row from the colors channel
If colors is disabled or pinned, each line corresponds to one row from the text channel.
If text channel is pinned, and colors is enabled. each line corresponds to one row from the colors channel.
If both text or colors channels are pinned or text is pinned and colors are disabled, body is effectively useless and can be omitted.
Body example:
@body
<=>\ 112228111111
,..\\.., 111118811111
' // ' 111991111111
| |111111111111
| |111111111111
'.__.~._.' 111111811111
<=>\ 112228111111
,..\\.., 111118811111
' //,_.--' 11199fffff11
| / { 1111f11f1111
| \_,''-. 1111fffffff1
'.__.~._.' 111111811111
<=>\ 112228111111
,..\\.., 111118811111
',--,_.--' 11fffffffff1
} { ffffffffffff
,-,_,''-. fffffffffff1
'.__.~._.' 111111811111
An optional @colors-pin block can be used to pin a single frame for the colors
channel.
It consists of a single frame with one row per line.
Example:
@color-pin
112228111111
111118811111
111991111111
111111111111
111111111111
111111811111
An optional @text-pin block can be used to pin a single frame for the text
channel.
It consists of a single frame with one row per line.
Example:
@text-pin
<=>\
,..\\..,
' // '
| |
| |
'.__.~._.'
An optional @attach block can be used to provide arbitrary data attached to
art. It consists of single line with any text, but typically JSON.
This block is added for compatibility with "extra" key in durformat.
Example:
@attach
{ "key": "value" }
The 3a format can be extended with custom blocks and header keys. Extension names must follow the Java package naming convention.
Recommendations for automatic formatting and optimization of 3a files:
- Strip redundant header keys and block titles unless specified otherwise below.
- Preserve comments unless the user explicitly requests they be removed.
- Comment lines in the header that immediately precede a key line are considered "attached" to that key and should be moved together when reformatting. Keys with attached comments should not be stripped even if they would otherwise be redundant.
- Remove all content that should be ignored during parsing (e.g.,
\rcharacters, frame-specific delay values for non-existent frames), except for comments which should be preserved as above. - Convert case-insensitive strings to lowercase.
Suggested MIME type for 3a is text/x-aaa.
The current version of 3a can be distinguished from the
legacy format by the presence (or absence) of
width & height parameters in the legacy header.
The 3a specification fully supports Unicode, but not all rendering software (terminal emulators, etc.) supports all Unicode features. To maximize compatibility:
- Avoid using grapheme clusters that contain more than one code point in art.
- Avoid features added in very recent Unicode releases if broad compatibility is required.