JAI: Just AI Images

Living Document,

This version:
https://jai.gnist.ai/spec/
Editor:
André C. Andersen (Gnist AI)

Abstract

JAI compresses an image into words: a natural-language description plus optional rendering parameters, from which a generative model reconstructs the picture. It is designed for a world where generating images is cheap and local and moving them is not. This specification defines the data model, the human-readable text form, the JSON form, the gzip-compressed file form (.jai, image/jai), the standard parameters and how renderers interpret them, integration with HTML and URLs, and conformance requirements for parsers, serialisers, validators, renderers and viewers.

Edition 1.0-draft. This is a living document; the edition stamp identifies the vocabulary and fixture set that the Agent Skill [AGENT-SKILLS] and the conformance suite refer to.

1. Introduction

This section is informative.

A JAI document is an image compressed into words: a natural-language description and, optionally, a few rendering parameters, from which a generative model reconstructs the picture. The description is the code; the model is the decoder. Two decoders produce different pixels, but the same image in meaning and appearance, in the way two JPEG decoders produce slightly different pixels from the same file, only much further along the same curve.

The simplest JAI document is a sentence:

A magical castle

Parameters follow the description, separated by semicolons:

jai:A magical castle;style=anime;model=flux

The same document has a JSON form and a compressed file form, and all three are the same document:

Text form:
jai:A magical castle;model=flux;style=anime
JSON form:
{"jai":1,"prompt":"A magical castle","model":"flux","style":"anime"}
File form: the JSON above as a single gzip member, in a file named castle.jai, served as image/jai, or embedded as data:image/jai;base64,….

1.1. Motivation

This section is informative.

1.1.1. A description is compression

Every image format trades bits for distortion. JPEG discards detail the eye will not miss; JAI keeps only what a person would say about the picture and discards the rest. In the terms of rate–distortion theory it sits at the far low-rate end of the same curve: a typical document is 50 to 500 bytes for an image of any resolution, against tens of kilobytes to megabytes for a raster, and its distortion is measured in meaning and appearance (does the result show the same things, arranged and lit the same way) rather than in pixel error.

This works because the decoder carries a prior. A generative model has learned what harbours, castles and dusk light look like, so the code only has to say which ones, where and how. Speech coding has relied on the same idea for decades: a vocoder transmits the parameters of a model of the voice instead of the waveform, and a CELP encoder finds those parameters by running the decoder inside the encoder and keeping what sounds closest. JAI encoding is the same analysis by synthesis (§ 10 Encoding images): an image encoder describes an image, renders the description with the target models, compares the renders with the original, and revises the description until they match as closely as time and budget allow. A document can carry a core description shared by all models plus small per-model refinements, so that each decoder in its preference list reconstructs the image as well as it can.

The rate–distortion trade-off is the author’s to choose: a sentence for a decorative image, a few precise paragraphs for one whose look matters.

1.1.2. Where it matters

JAI is designed for a world where generating an image is cheap and local, and moving one is not.

Today, rendering usually runs on hosted providers through an API key. That is the stage this edition is published in: good for demonstrations and for personal renders, and the reason the security section spends so much care on consent, budgets and what leaves the browser (§ 13 Security and privacy considerations). The format does not depend on it.

1.2. What JAI is and is not

This section is informative.

JAI describes visual intent, not pixels. It does not specify a neural network, a sampler, a provider, or exact output. A decoder produces an image consistent with the description, not the image; the encoder can only reduce the distortion, never remove it. Decoding is non-deterministic across decoders, versions, hardware and time. With hosted providers, as today, it may also take seconds, cost money, and be refused by a provider’s content policy, and it has side effects that no other image format has: it transmits the document, and possibly an input image, to a renderer, under the account of the person viewing it.

Every JAI document also has a textual rendering that is instant, free and always available: its description, the prompt values joined with line breaks. A user agent that will not render pixels presents that instead.

Where exact pixels matter more than size, such as evidence, medical images, diagrams and screenshots of text, ship the raster. JAI can still travel alongside it as the editable, searchable source: HTML’s <picture> element carries both (§ 11.1 Carriers).

1.3. Audiences

This section is informative.

This specification is written for six audiences: people and AI agents who write JAI; authoring tools that convert images to JAI or validate it; library developers implementing parsers and serialisers; extension developers adding parameters or renderers; browser and extension developers implementing discovery, rendering and replacement; and renderer developers turning a JAI document into a picture. Sections § 5 Text form through § 7 File form, media type and decoding are the parts a library developer needs; § 8 Standard parameters and § 9 Rendering are for renderer developers; § 11 HTML and URL integration is for browser, extension and web developers; and § 15 Writing JAI by hand is a plain-language primer for authors.

2. Conformance

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. The same words in lower case have their ordinary English meaning.

Everything in this specification is normative except sections marked informative, examples, notes, and the appendices so marked.

2.1. Conformance classes

This specification defines requirements for the following classes. An implementation may belong to several.

conforming document

A JAI document in any of its forms that satisfies every requirement placed on documents and authors (§ 4 Data model, § 5 Text form, § 6 JSON form, § 7 File form, media type and decoding, § 8 Standard parameters, § 11 HTML and URL integration).

parser

Converts the text form to a document (§ 5.4 Parsing).

JSON reader

Converts the JSON form to a document (§ 6.3 Reading).

decoder

Converts the bytes of a .jai file, an image/jai resource, or the body of a data:image/jai URL to a document (§ 7.4 Decoding). A decoder contains a parser and a JSON reader.

serialiser

Produces the canonical text form and the canonical JSON form of a document (§ 5.5 Canonical text serialisation, § 6.4 Canonical JSON).

encoder

Produces the file form (§ 7.2 Encoding). An encoder contains a serialiser.

validator

Reports advisory diagnostics about a document or its carrier (§ 14 Diagnostics).

interpreter

Derives the interpreted parameters of a document from its values (§ 8.1 Interpretation). Every renderer and every viewer contains an interpreter.

renderer

Produces an image from a document (§ 9 Rendering).

viewer

Software acting for a user that discovers JAI documents in content, decides whether and when to render them, and presents the result: a browser, a browser extension, a chat client, an image viewer (§ 11 HTML and URL integration, § 11.5 Viewer requirements, § 13 Security and privacy considerations).

authoring tool

Software that produces or edits JAI documents on behalf of an author.

2.2. Determinism

Parsing, reading and decoding are total and deterministic: for every input, including malformed input, a conforming implementation produces a document (possibly empty) and a set of diagnostics, and two conforming implementations produce the same document and the same parser-level diagnostics for the same input. Error recovery is therefore specified, not left to implementations. Nothing in this specification may throw, abort or produce a partial result on malformed input. The guarantee covers inputs within the minimum bounds of § 13.1 Untrusted input and bounds; beyond them, an implementation that applies its bound yields the empty document with the diagnostic named there.

2.3. Conformance test suite

The repository at github.com/gnistai/jai holds machine-readable fixtures under tests/ (§ 14.1 Conformance fixtures). The fixtures are normative: where the fixtures and the prose of this specification disagree, the fixtures are correct and the prose has a bug. An implementation may claim conformance for a class when it passes every fixture of that class ("JAI 1.0-draft conforming parser").

Conformance class Fixture classes it must pass
parser parse (documents and diagnostics)
JSON reader json
decoder parse, json, decode, and the url cases of kind data
serialiser serialize (text, bare, json, identity), and roundtrip except the file form
encoder as serialiser, plus serialize url and the file form of roundtrip
interpreter interpret
viewer html, url (all kinds), and everything required of a decoder and an interpreter
validator the advisories and no_advisories of every case

Renderers, authoring tools and conforming documents have no fixtures in this edition.

3. Terminology

document

An instance of the data model (§ 4 Data model): a map from keys to lists of values, describing one image.

key

The name of a parameter: an ASCII string matching the key grammar.

value

One string assigned to a key. A key may have several values, in order.

parameter

A key together with its values.

standard key

One of the eleven keys defined in § 8 Standard parameters.

prompt

The values of the key prompt: the description of the image.

description

The textual rendering of a document: its prompt values joined with U+000A LINE FEED, in order.

text form

The human-readable serialisation of a document (§ 5 Text form): jai:A magical castle;style=anime.

JSON form

The JSON serialisation of a document (§ 6 JSON form).

file form

The gzip-compressed JSON form (§ 7 File form, media type and decoding); the content of a .jai file.

canonical form

The unique text form or JSON form a serialiser produces for a document (§ 5.5 Canonical text serialisation, § 6.4 Canonical JSON).

identity

The SHA-256 digest of a document’s canonical JSON form (§ 6.5 Identity).

JAI whitespace

Exactly the 25 code points that have the Unicode White_Space property: U+0009 TAB, U+000A LF, U+000B VT, U+000C FF, U+000D CR, U+0020 SPACE, U+0085 NEL, U+00A0 NO-BREAK SPACE, U+1680 OGHAM SPACE MARK, U+2000 to U+200A inclusive, U+2028 LINE SEPARATOR, U+2029 PARAGRAPH SEPARATOR, U+202F NARROW NO-BREAK SPACE, U+205F MEDIUM MATHEMATICAL SPACE and U+3000 IDEOGRAPHIC SPACE. No other code point is whitespace for the purposes of this specification. U+FEFF is not whitespace.

trim

To remove every leading and trailing JAI whitespace code point from a string.

String.prototype.trim (JavaScript), str.strip() (Python), String.trim() (Java) and the regular-expression class \s each remove a different set of code points, and none of them removes exactly this set; Go’s strings.TrimSpace happens to match. Implementations use the list.
ASCII lowercase

The string with every code point in the range U+0041 (A) to U+005A (Z) replaced by the code point 32 greater, and no other change [INFRA].

ASCII case-insensitive

Two strings match ASCII case-insensitively when their ASCII lowercase forms are identical [INFRA]. Implementations MUST NOT use locale-sensitive case mapping or Unicode case folding anywhere in this specification: under those, U+0130 (İ), U+0131 (ı) and U+212A (KELVIN SIGN) would match ASCII letters.

scalar value string

A string of Unicode scalar values, that is, a string containing no surrogate code points [INFRA].

author

Whoever wrote a document.

publisher

The site or channel that delivers a document to a user. On a site that accepts user content the author and the publisher are different parties.

user

The person on whose behalf a viewer renders a document, and under whose renderer account the pixels are produced.

original image

Pixels that accompany a document in its carrier: the <img> element’s own image in HTML, or the image named by the source key (§ 8.1.7 source).

carrier

A place in a host format where a document is found (§ 11.1 Carriers).

JAI context

The content of a .jai file, of a resource served as image/jai or referenced from a carrier, or the body of a data:image/jai URL. Attribute carriers are not JAI contexts: they always hold the text form.

provider

A service that runs a model for a renderer.

layout box

The box HTML lays out for an element, from its attributes and CSS.

advisory

Of a parameter: a preference the author expresses, which a renderer or viewer honours when it can and ignores, with a diagnostic, when it cannot; an advisory value never selects anything the user has not configured and never overrides a user setting.

4. Data model

A document is an ordered map from keys to lists of one or more values. The order of values within a key is significant; the relative order of distinct keys is not significant and carries no meaning.

A document describes exactly one image.

4.1. Keys

The key grammar is, in ABNF [RFC5234]:

key      = name [ "/" target ]
name     = segment *( "." segment )
target   = segment
segment  = ALPHANUM *( ALPHANUM / "_" / "-" )
ALPHANUM = %x61-7A / DIGIT          ; a-z, 0-9

A key is a name, a non-empty sequence of segments separated by single full stops, optionally followed by / and a target, one segment. Each segment starts with a lowercase ASCII letter or digit and continues with lowercase ASCII letters, digits, _ and -. A key with a target, such as prompt/flux, is a qualified key: it holds refinements for the models that target names (§ 8.3 Per-model refinements). Only the names prompt, style and avoid take a target in this edition; parsers and serialisers keep other qualified keys unchanged, and validators report them with invalid-qualifier.

Keys are compared for equality after ASCII lowercase conversion: readers convert every key they receive to ASCII lowercase before anything else, so a document never contains two keys that differ only in case.

Keys without a . are reserved for this specification (§ 12 Extensibility).

The key jai is reserved and is never a parameter: in the JSON form the member named jai identifies the object as a JAI document (§ 6.2 The jai marker); in the text form an assignment to jai is dropped (§ 5.4 Parsing).

Implementations that use language objects as maps need real maps or null-prototype objects: constructor is a valid key, and __proto__, while not a valid key, is a valid JSON member name.

4.2. Values

A value is a non-empty scalar value string that does not begin or end with JAI whitespace and does not contain U+000D CARRIAGE RETURN or U+0000 NULL.

Every reader of every form applies the following value normalisation to each candidate value before it enters the document, in this order:

  1. Replace every surrogate code point with U+FFFD REPLACEMENT CHARACTER.

  2. Replace every U+000D U+000A pair, and then every remaining U+000D, with U+000A.

  3. Replace every U+0000 with U+FFFD. (Other control characters are kept as content; validators report them, see § 14 Diagnostics. Byte inputs never reach this step with U+0000: § 7.4 Decoding rejects the byte 00.)

  4. Trim the string.

  5. If the result is empty, discard the candidate and emit the diagnostic empty-value; otherwise the result is the value.

Values are strings and only strings. Numbers, booleans and structured data exist only as their spelling (strength=0.6, animate=true); this is what makes the text form able to represent every document.

No Unicode normalisation is applied anywhere in this specification. Two strings are the same value if and only if they consist of the same sequence of code points; canonically equivalent texts (for example NFC and NFD spellings of the same word, or U+037E GREEK QUESTION MARK and ;) are different values and yield different identities. Parsers MUST NOT normalise their input.

4.3. Standard keys and repetition

A document may assign any number of values to any key; the data model does not know which keys are single-valued. The standard keys aspect, mode, strength, seed, source, preview and animate are single-valued at interpretation: an interpreter uses the last value and reports repeated-single (§ 8.1 Interpretation). Parsers and serialisers keep every value; identity and round-tripping depend on all of them.

5. Text form

The text form is a string. It is used wherever a string fits: HTML attributes, the body of a data:image/jai URL, chat messages, clipboards and documentation.

5.1. Grammar

*This grammar is informative; the algorithm in § 5.4 Parsing is normative.*

jai-text   = OWS [ prefix ] [ text-segment *( ";" text-segment ) ] [ ";" ]
prefix     = %s"jai:" OWS                  ; "jai:" is ASCII case-insensitive
text-segment = assignment / prose
assignment = OWS key-ci OWS "=" value   ; key-ci: the key grammar, ASCII case-insensitive
prose      = value
value      = *( escape / vchar )
escape     = "\" ( ";" / "=" / "\" )
vchar      = <any scalar value except ";" and "\"> / "\" <not followed by ";", "=" or "\">
OWS        = *( <JAI whitespace> )

A segment is an assignment if it matches assignment; otherwise it is prose. A \ that does not begin an escape is an ordinary character.

5.2. Structure

Input Document
A magical castle prompt = A magical castle
jai:A magical castle;style=anime;model=flux;model=sdxl prompt = A magical castle; style = anime; model = flux, sdxl
prompt=A magical castle prompt = A magical castle
model=flux;prompt=A magical castle model = flux; prompt = A magical castle
A wizard stands in a doorway; behind him, a dragon sleeps;model=flux prompt = A wizard stands in a doorway; behind him, a dragon sleeps; model = flux
A castle ; style = anime ; prompt = A castle; style = anime
source=data:image/webp;base64,UklGRkI=;style=anime source = data:image/webp;base64,UklGRkI=; style = anime (base64,… is not an assignment)
; my key=value prompt = my key=value (my key contains a space and is not a key; the leading ; yields empty-segment)
http://example.org/?a=b prompt = http://example.org/?a=b (: and / are not key characters)
Draw a graph\; y=2x prompt = Draw a graph; y=2x
E\=mc2 explained prompt = E=mc2 explained

5.2.1. Sharp edges

This section is informative.

The assignment rule is deliberately simple, and five consequences are worth knowing. Validators report each of them (§ 14 Diagnostics).

  1. A description that begins with a word, an equals sign and more text is an assignment: E=mc2 explained assigns mc2 explained to the key e. Write prompt=E=mc2 explained or E\=mc2 explained.

  2. Prose after an assignment continues that assignment, not the prompt: style=anime;A castle has no prompt and the style anime;A castle. Write the description first, or introduce it with prompt=. A new line does not start a parameter either; one-parameter-per-line layouts need ; at the line ends.

  3. Whitespace is allowed around =, so A chalkboard; e = mc2 written on it assigns to e. Write e \= mc2.

  4. A data: URL whose media type carries parameters (;charset=utf-8, ;name=a.png) contains ;key= and is split there. Escape those semicolons (data:image/svg+xml\;charset=utf-8,…); the canonical serialiser does so. A plain data:…;base64, URL needs nothing, because base64,… is not an assignment.

  5. A word, /, a word and = after a semicolon is a qualified key: A ratio; a/b=c assigns c to a/b. Write a/b\=c to keep it as text.

5.3. Escaping arbitrary text

An authoring tool or template that places text it did not write into a document MUST protect it. The function escape-prose(s) replaces every \ with \\, then every ; with \; and every = with \=. For any string s whose value normalisation is s itself, the segment prompt=escape-prose(s) parses as exactly one prompt value equal to s, and key=escape-prose(s) as one value of key.

The prompt= is part of the recipe: a bare first segment that begins with jai: would lose those characters to prefix removal.

This escaping is in addition to the host format’s own: HTML attribute quoting, JSON string escaping, percent-encoding in URLs. Leading and trailing whitespace and carriage returns in s are not preserved, because values never contain them.
A template inserting a product description:
data-jai="prompt=Red mug\; model\=XZ-200\; colour\=red;style=product photo"
parses to prompt = Red mug; model=XZ-200; colour=red and style = product photo. Without escaping, model and colour would have become parameters and the prompt would have been cut short.

5.4. Parsing

The text parsing algorithm takes a string input and returns a document and a list of diagnostics. Transport decoding (percent-decoding, HTML attribute decoding) has already happened (§ 11.2 Transport decoding); the algorithm never decodes anything.

  1. Let diagnostics be an empty list and doc an empty document.

  2. Replace every surrogate code point in input with U+FFFD.

  3. While input begins with U+FEFF, remove it.

  4. Replace every U+000D U+000A pair in input, then every remaining U+000D, with U+000A.

  5. Skip leading JAI whitespace: let i be the index of the first code point of input that is not JAI whitespace, or the length of input.

  6. If input from i begins with the four code points jai: compared ASCII case-insensitively, advance i past them.

  7. Split input from i into raw segments: scan left to right; a \ followed by any code point consumes both code points as part of the current raw segment; an unescaped ; ends the current raw segment and starts a new one; the end of the input ends the last raw segment. Raw segments keep their escape sequences and their whitespace.

  8. Let pending be an empty list of (key, raw text) pairs and let current be null.

  9. For each raw segment raw, in order:

    1. If raw is empty or consists only of JAI whitespace: unless raw is the last raw segment, append empty-segment to diagnostics; in either case continue with the next raw segment. (A trailing delimiter is allowed and not diagnosed; an interior empty segment is ignored with a diagnostic.)

    2. Let eq be the index of the first = in raw that is not preceded by a \ that is part of an escape sequence, determined by the same left-to-right scan as step 7.

    3. If eq exists and the trimmed text of raw before eq matches the key grammar after ASCII lowercase conversion:

      1. Let key be that ASCII-lowercased text and rest the text of raw after eq.

      2. If key is jai: append reserved-key to diagnostics; set current to a discarded pair that is not in pending; continue.

      3. Append the pair (key, rest) to pending and set current to it.

    4. Otherwise, if current is null: append the pair (prompt, raw) to pending and set current to it.

    5. Otherwise: append ; followed by raw to the raw text of current. (If current is the discarded pair, the text is dropped with it.)

  10. For each pair (key, text) in pending, in order:

    1. Let unescaped be text with every escape sequence \;, \=, \\ replaced by its second character, scanning left to right so that \\; (two backslashes, then a semicolon) yields \ followed by an unescaped ;.

    2. Apply value normalisation to unescaped. If it yields a value, append that value to the list for key in doc; otherwise the diagnostic empty-value has been emitted.

  11. Return doc and diagnostics.

Trimming happens once, in step 10, so interior spacing around restored semicolons is preserved exactly as typed (A wizard ; behind stays A wizard ; behind), except that an ignored empty or whitespace-only segment disappears together with one delimiter (A; ;b becomes A;b; write A\; \;b to keep it).

The result is deterministic for every input and the algorithm runs in time linear in the length of the input.

Input Document Diagnostics
jai: empty
;; empty empty-segment ×2
A castle;style= prompt = A castle empty-value
k=;a k = ;a (the empty assignment was continued)
jai:jai=1;prompt=x prompt = x reserved-key
jai:A castle;jai=2;more prompt = A castle (more continued the dropped assignment) reserved-key
jai:A castle prompt = A castle
jai: jai:A castle prompt = jai:A castle (the prefix is removed once)
JAİ:A castle (U+0130) prompt = JAİ:A castle (not the prefix)
a\=b prompt = a=b
A; ;b prompt = A;b empty-segment
a\\;style=x prompt = a\; style = x
a\;style=x prompt = a;style=x
mode=transform;style=anime mode = transform; style = anime (a document without a prompt is valid)

5.5. Canonical text serialisation

A serialiser takes a document whose keys and values satisfy § 4 Data model and produces one string. Serialisers MUST reject or normalise input that does not satisfy the data model (keys not lowercase, values not normalised).

  1. Let out be jai:.

  2. Let segments be an empty list.

  3. For each value v of the key prompt, in order: if it is the first prompt value and v does not match the assignment pattern (optional JAI whitespace, a key, optional JAI whitespace, =, matched ASCII case-insensitively at the start of v), append escape-value(v) to segments; otherwise append prompt= followed by escape-value(v).

  4. For each qualified key whose name is prompt, in ascending order of key by code point, for each of its values v in order: append the key, =, and escape-value(v) to segments.

  5. For each remaining key, in ascending order of key by code point, for each of its values v in order: append the key, =, and escape-value(v) to segments.

  6. Join segments with ;, append the result to out, and return out.

escape-value(v) is one left-to-right pass over v that inserts a \ before:

Each decision looks at the original v, never at text already escaped. As an ECMAScript regular expression, with WS standing for the JAI whitespace class and KEY for the key grammar (case-insensitively):

v.replace(/\\(?=[;=\\]|$)|^;|;(?=WS*(?:$|;|KEY WS*=))/gi, m => '\\' + m)

WS and KEY are placeholders for the class and the pattern to substitute.

Implementations that escape in two passes MUST escape backslashes first. = is never escaped by the serialiser; the prompt= form protects the one place where it matters.

This escaping is deterministic and regular; it escapes a superset of the characters that would be significant and is not the shortest possible encoding. The canonical form of ordinary prose stays readable:

Document Canonical text
prompt = A wizard stands; behind him, a dragon; model = flux jai:A wizard stands; behind him, a dragon;model=flux
prompt = E=mc2 explained jai:prompt=E=mc2 explained
prompt = ;-) a smiley jai:\;-) a smiley
prompt = ends with backslash\; style = x jai:ends with backslash\\;style=x
prompt = a;;b jai:a\;;b
prompt = A list; item=1 jai:A list\; item=1
prompt = C:\Users\me jai:C:\Users\me
style = anime; prompt = A castle; model = flux, sdxl jai:A castle;model=flux;model=sdxl;style=anime

Parsing the canonical text of a document yields that document, and serialising the result yields the same text (the canonical form is a fixed point). The conformance suite checks both properties.

5.5.1. Bare serialisation

The bare serialisation is the canonical text without the jai: prefix, for contexts that establish JAI and are not displayed as text (the body of a data:image/jai URL, a data-jai attribute). It is produced by the same algorithm with one more case in step 3: the first prompt value is also written as prompt=… when it begins with jai: (ASCII case-insensitively), with U+FEFF, or with {.

The { rule keeps the body of a data:image/jai URL from being mistaken for the JSON form by a decoder (§ 7.4 Decoding).

5.6. Bidirectional text

This section is informative.

The structure of the text form is left-to-right: keys and delimiters are ASCII. A canonical string always starts with jai:, which pins the base direction of the whole string under first-strong detection; an Arabic or Hebrew prompt therefore displays correctly in a plain text field. The bare form, used only in attributes and URL bodies, is not displayed as text. Authoring tools that show a document should display the canonical string in a left-to-right container and each value with dir="auto" or unicode-bidi: isolate. Trailing punctuation of a right-to-left prompt may display beside the following ;key=; parsing is unaffected. Validators report explicit bidirectional controls and invisible formatting characters inside values (§ 14 Diagnostics).

6. JSON form

The JSON form is a JSON object [RFC8259].

{"jai":1,"prompt":"A magical castle","model":["flux","sdxl"],"style":"anime"}

6.1. Shape

6.2. The jai marker

The member jai identifies a JSON object as a JAI document and gives the file form its decompressed signature: the bytes {"jai":1 followed by , or } (§ 6.4 Canonical JSON). Its value is the JSON number 1 (so 1.0 is accepted, because it is the same number).

This specification does not expect to assign another value to the marker: revisions add keys and reserved member shapes, and never change the meaning of existing ones. An incompatible future format would use a different member name or media type.

Readers behave as follows:

The text form carries no marker; its prefix plays that role.

6.3. Reading

The JSON reading algorithm takes a JSON text and a flag in JAI context and returns a document and diagnostics.

  1. Parse the text as JSON [RFC8259]. Texts that are not valid RFC 8259 JSON (including NaN, Infinity, comments and trailing commas), and texts containing a number whose value, rounded to the nearest IEEE 754 binary64 value, is infinite (for example 1e400) wherever it occurs, the jai member included, yield the empty document with the diagnostic invalid-json. If the top-level value is not an object, yield the empty document with invalid-json. If arrays and objects are nested more than 32 deep anywhere in the text (the top-level object has depth 1), yield the empty document with invalid-json. Implementations whose JSON parser recurses check this with a scan before parsing, skipping the contents of strings, so that no input exhausts the stack.

  2. Read the members into an insertion-ordered map keyed by the exact member name. A member name that repeats an earlier one replaces that member’s value in place and keeps the earlier position (this is what ECMAScript JSON.parse and Python json.loads do). Readers do not diagnose an exact repetition; validators MAY report it as duplicate-member.

  3. Handle the marker as in § 6.2 The jai marker, then remove it from the map. If the marker is absent and in JAI context is false, return the empty document and no diagnostics.

  4. For each remaining member, in map order:

    1. Let name be the ASCII lowercase of the member name. If name does not match the key grammar, emit invalid-key and skip the member. If name is jai, emit duplicate-member and skip it.

    2. Let items be the member value if it is an array, otherwise a one-element list containing the value.

    3. For each item: a string is a candidate value; a number is converted to a candidate value by the ECMAScript Number::toString algorithm applied to its IEEE 754 binary64 value, as RFC 8785 §3.2.2.3 specifies ([RFC8785]), with the diagnostic number-value; true and false become the candidates true and false with number-value; null is skipped with empty-value; an object or an array is skipped with unsupported-member.

    4. Apply value normalisation to each candidate and append the resulting values, in order, to the list for name. If a member earlier in the map has a different name that also lowercases to name, emit duplicate-key for name, once for this member, whether or not either member contributed values. The values of all such members are concatenated in map order.

  5. Return the document and diagnostics.

Number conversion follows ECMAScript so that a hand-written "strength": 0.6 is forgiven, and so that it is the same in every language: 1.0 → 1, 1E2 → 100, 0.60 → 0.6, -0 → 0, 1e21 → 1e+21, 5e-324 → 5e-324. Integers beyond 253 lose precision in this conversion; authors write such values as JSON strings (§ 8.1.6 seed requires it). Python readers need parse_constant to reject NaN and Infinity and a short emulation of Number::toString; the conformance suite pins the cases.
JSON Document Diagnostics
{"jai":1,"prompt":" A castle "} prompt = A castle
{"jai":1,"prompt":"a\r\nb"} prompt = a U+000A b
{"jai":1,"Prompt":"a","prompt":"b"} prompt = a, b duplicate-key
{"jai":1,"strength":0.60,"animate":true} strength = 0.6; animate = true number-value ×2
{"jai":1,"seed":12345678901234567890} seed = 12345678901234567000 number-value (precision lost)
{"jai":1,"prompt":["a",null,"",["b"],{"x":1}," c "]} prompt = a, c empty-value ×2, unsupported-member ×2
{"jai":"1","prompt":"x"} prompt = x invalid-marker
{"jai":2,"prompt":"x"} prompt = x unsupported-version
{"prompt":"x"} in a .jai file prompt = x missing-marker
{"jai":1,"__proto__":"x","prompt":"x"} prompt = x invalid-key

6.4. Canonical JSON

The canonical JSON form of a document is the following byte sequence, and every serialiser produces exactly it:

Because values are scalar value strings, canonical JSON never contains surrogate escapes.

This is RFC 8785 (the JSON Canonicalization Scheme) with one deviation: the members jai and prompt come first, so that every file begins with the bytes {"jai":1 followed by , or } after decompression and a reader can see the description first. An implementation can run a JCS library and move those two members to the front; the string and number rules are identical. In JavaScript the canonical form is JSON.stringify of an object built in this member order; in Python, json.dumps(obj, ensure_ascii=False, separators=(',', ':')) with members inserted in order; Go callers need SetEscapeHTML(false).
Document: style = anime; prompt = A castle, with a moat; model = flux.
{"jai":1,"prompt":["A castle","with a moat"],"model":"flux","style":"anime"}

6.5. Identity

The identity of a document is the SHA-256 digest of its canonical JSON form, written as 64 lowercase hexadecimal digits.

Identity is syntactic: two documents are the same document if and only if their canonical JSON forms are byte-identical. Documents that differ in any member, including preview, vendor keys, or the spelling 16x9 versus 16:9, are different documents. Identity is the same whichever form or carrier delivered the document, and whatever whitespace, key case or key order the author used.

Identity names a document, not a render. A render cache key MUST include, besides the identity, the renderer and its configuration, and the identity of every image input the render used (an explicit source or the carrier’s original image). See also § 13.7 Caches.

Identity is public information and MUST NOT be used as a capability or as a key into an allow- or deny-list.

7. File form, media type and decoding

7.1. File form

The file form of a document is exactly one gzip member [RFC1952] with compression method 8 (deflate), followed by nothing, whose decompressed content is the canonical JSON form. A .jai file contains the file form.

Decoders MUST reject, as described in § 7.4 Decoding:

The header fields FEXTRA, FNAME, FCOMMENT, MTIME, XFL and OS are read and ignored; FNAME is never used as a file name. Byte equality of two .jai files is not a conformance notion: the canonical form is defined at the JSON level.

7.2. Encoding

An encoder produces the file form from a document. Encoders MUST write the canonical JSON form as the member content. Encoders SHOULD write MTIME as 0 and SHOULD NOT write FNAME or FCOMMENT, so that files carry no timestamps or file names and tests are reproducible.

gzip -n and the default settings of most library encoders already satisfy this. A file produced by gzip -c castle.json > castle.jai from canonical JSON is a conforming file whatever its header fields.
Why gzip, when a forty-byte description becomes an eighty-byte file: gzip is the one compression every browser (DecompressionStream), runtime, language and CDN decodes natively; it undoes almost all of the base64 inflation of an embedded preview; and it gives the format one encoding rule instead of "compress when large". Contexts where the twenty extra bytes matter use the text form in a data: URL instead (§ 7.5 data: URLs).

7.3. Media type and file extension

The media type of a JAI document in any of its three serialisations is image/jai. The file extension is .jai. The type attribute value image/jai (§ 11.1 Carriers) is matched by its essence, ASCII case-insensitively, ignoring parameters. A charset parameter has no meaning: the text and JSON forms are always UTF-8.

image/jai is used unregistered, as image/webp was for a decade before RFC 9649. Appendix Appendix A. Media type registration template carries the registration template; registration in the standards tree requires an IETF or SDO process that the project intends to pursue once the format is stable. Should the type be registered under a different name, viewers will match both. JAI is unrelated to JPEG AI (ISO/IEC 6048), whose registered types are image/jaii and image/jais.

Servers SHOULD send Content-Type: image/jai for .jai resources, and, as for web fonts, SHOULD send Access-Control-Allow-Origin: * so that scripts on other origins can read them; a .jai resource contains no user data. Servers need not apply Content-Encoding: gzip; if they do, fetch() removes it and the decoder sees JSON. Decoders apply § 7.4 Decoding to the bytes of any resource referenced from a JAI carrier regardless of the served Content-Type, because static hosts commonly serve unknown extensions as application/octet-stream.

7.4. Decoding

The decoding algorithm takes a byte sequence bytes and a flag in JAI context (true in a JAI context) and returns a document and diagnostics. It never falls back from one serialisation to another; each layer either succeeds or yields the empty document.

  1. If bytes begins with 1F 8B:

    1. Decode exactly one gzip member under the output bound. If the member is malformed in any way listed in § 7.1 File form, if bytes follow it, or if the bound is exceeded, return the empty document with malformed-container, discarding any partial output.

    2. Let bytes be the decompressed content, and let from gzip be true.

  2. If from gzip is false and bytes is longer than the decoder’s text bound (§ 13.1 Untrusted input and bounds), return the empty document with undecodable.

  3. Decode bytes as UTF-8. If the bytes are not well-formed UTF-8 [ENCODING] or contain the byte 00, return the empty document with malformed-container if from gzip is true and with undecodable otherwise. There is no autodetection of UTF-16 or other encodings and no charset handling. (The byte 00 never occurs in text a person or tool writes; rejecting it keeps binary data, such as UTF-16 without a byte order mark, from becoming a prompt.)

  4. Remove every leading U+FEFF.

  5. Let i be the index of the first code point that is not JSON whitespace (U+0009, U+000A, U+000D, U+0020). If the code point at i is { and the next code point after i that is not JSON whitespace is " or }, the input is in the JSON form: run the JSON reading algorithm on the text with in JAI context. A JSON failure is final: do not try the text form.

  6. Otherwise, if from gzip is true, return the empty document with malformed-container (a gzip member must contain JSON).

  7. Otherwise, the input is in the text form: run the text parsing algorithm on the text.

A .jai file whose bytes are not gzip is accepted by this algorithm but is not a conforming file; validators report non-canonical-encoding.

The three serialisations are distinguishable at the first non-whitespace bytes: 1F 8B, { followed by " or }, or anything else. A text description that begins with { but is not followed by a quote, for example {masterpiece}, a castle, is text. The bare serialisation writes a {-initial prompt as prompt=… so that canonical output never meets the JSON test.
Bytes Result
gzip member containing {"jai":1,"prompt":"A castle"} prompt = A castle
the same followed by a second gzip member empty, malformed-container
gzip member containing jai:A castle empty, malformed-container
{"jai":1,"prompt":"A castle"} (uncompressed) prompt = A castle; validator: non-canonical-encoding
{masterpiece}, a castle prompt = {masterpiece}, a castle
{"quoted"} prose empty, invalid-json
FF FE 6A 00 … (UTF-16) empty, undecodable
the bytes of a PNG file empty, undecodable
jai:A castle prompt = A castle

7.5. data: URLs

A document may be embedded in a data: URL [RFC2397] with the media type image/jai, in any of the three serialisations:

data:image/jai,A%20magical%20castle;style=anime
data:image/jai;base64,H4sIAAAAAAACA6tWykrMVLIy1FEqKMrPLShRslJyVMhNTM9MTsxRSE4sLslJVdJRKi6pBNJWSol5mbmpSrUAq6nXdjUAAAA=
Both URLs carry the same document.

The body of the URL, after the data: URL processor of the Fetch Standard has percent-decoded or base64-decoded it [FETCH], is decoded with § 7.4 Decoding with in JAI context true (the body is a JAI context). The non-base64 text form is the most compact and the only human-readable option; the base64 file form is permitted and never required. Media-type parameters other than base64 are ignored.

If the data: URL processor returns failure (no ,, or a ;base64 body that forgiving-base64 rejects), or the essence of its MIME type is not image/jai, the URL carries no document. The MIME type is parsed, as Fetch does, from the serialised URL, in which the URL parser has percent-encoded non-ASCII and control code points, so data:image/jai followed by U+00A0 is not image/jai.

When a serialiser or encoder writes the text form or the JSON form into the body of a data:image/jai, URL it MUST percent-encode every byte of the UTF-8 encoding that is a C0 control, U+007F, an ASCII whitespace character (U+0020 as %20, U+000A as %0A, U+0009 as %09), % (as %25), # (as %23), " (as %22), ' (as %27), < (as %3C), > (as %3E), and a , that would be the last byte of the URL (as %2C); a body beginning with / is written with %2F. Every other byte, including ;, =, ,, {, }, ?, /, &, + and non-ASCII, MAY remain literal. The canonical URL form of a document is data:image/jai, followed by its bare serialisation so encoded, so that the canonical URL form can be placed in any quoted HTML attribute without further escaping.

Unencoded whitespace in a srcset attribute silently discards the candidate; an unencoded # ends the URL, so everything after it is lost; a literal % followed by two hexadecimal digits is decoded as a byte. Line breaks have to be %0A: every URL parser deletes raw tabs and line feeds. Percent-decoding is a transport step performed once by the URL processor; %3B and %3D become ; and = and then parse as delimiters, exactly as if typed. Only \; and \= protect prose (§ 5.3 Escaping arbitrary text).

8. Standard parameters

This edition defines eleven standard keys. Each has a meaning a renderer can act on without knowing anything about a particular model. All of them are advisory in the sense of § 3 Terminology: a renderer honours what it can and reports what it cannot (§ 9.4 Renderer diagnostics); none of them can select, fetch or configure anything the user has not configured.

Key Values Form Meaning
prompt many text What the image shows. The description is the values joined with U+000A, in order.
style many text Visual treatment: anime, watercolor, 35mm film photo. All values apply.
avoid many text Things or qualities the image should not contain, one per value, stated positively (people, not no people).
model many identifier Ordered preference list of models or rendering packages. A selector, never a locator.
aspect one W:H, WxH, decimal, square, landscape, portrait Preferred aspect ratio.
mode one generate, transform, keep How the description relates to an original image. Default generate.
strength one decimal 0 to 1, or percentage In transform, how far the result may depart from the source: 0 unchanged, 1 unconstrained.
seed one integer Reproducibility hint, passed through when the model accepts one.
source one URL or data: URL of a raster image The image to transform. Takes precedence over the carrier’s own image.
preview one data: URL of a tiny raster image Placeholder shown while rendering. Never an input to generation.
animate one boolean Requests a looping animated image where supported; otherwise a still frame.

8.1. Interpretation

An interpreter derives the interpreted parameters of a document from its values. Interpretation never modifies the document: the serialiser writes values as the author wrote them, and 16x9 and 16:9 remain different documents with the same interpretation.

For each single-valued standard key (aspect, mode, strength, seed, source, preview, animate) that is present:

  1. If the key has several values, let v be the last value and emit repeated-single for the key; otherwise let v be its value. Appending a parameter to a shared default string is how an author overrides a single-valued key. This does not work for prompt, style, avoid and model, whose values accumulate; this edition has no way to clear an inherited value (;style= is dropped with empty-value), so keep those keys out of shared prefixes.

  2. If v begins and ends with the same ASCII quotation mark (" or ') and is at least two code points long, remove both and trim the result (validators report quoted-value). Then, if v ends with ., remove it.

  3. Match v against the key’s value grammar below, ASCII case-insensitively where keywords are involved. On failure emit invalid-value for the key (for source, invalid-image; for preview, unsupported-preview), treat the key as absent, and, where the grammar suggests one, offer the corrected spelling in the diagnostic.

The values of prompt, style, avoid and model are used exactly as they appear in the document: no quotation mark or full stop is removed, and § 8.2 Model matching receives the document’s values.

Interpreters MUST NOT use lenient or locale-aware number parsers; the grammars below are ASCII and exact. Non-ASCII digits, full-width characters and the decimal comma are invalid (an authoring tool offers 0,6 → 0.6, 16:9 → 16:9).

8.1.1. prompt, style, avoid

Any value is valid. The interpreted prompt is the description: the values joined with a single U+000A in order. prompt=a;prompt=b renders as the same input as a single value a U+000A b but is a different document. Renderers MAY normalise whitespace for their model.

style values are combined; all apply. avoid values are independent items; avoid=text;avoid=people and avoid=text, people reach a negative prompt identically but are different documents. Validators report an avoid value that begins with no, not, without or never followed by whitespace (negated-avoid), because negation inside a negative prompt inverts the author’s intent on most models.

8.1.2. model

Any value is valid. Values are matched against the renderers the user has configured as described in § 8.2 Model matching. A value promises nothing: the same model value may select different models on different days or at different providers, and a value the user has not configured selects nothing.

Values are opaque identifiers. Validators MAY warn when a value looks like a URL or a file path (model-looks-like-locator), because such values are never fetched (§ 13.3 Selectors, never locators).

8.1.3. aspect

aspect  = ratio / number / "square" / "landscape" / "portrait"
ratio   = number OWS ( ":" / "x" / %x00D7 / "/" ) OWS number   ; ABNF strings are case-insensitive
number  = 1*DIGIT [ "." *DIGIT ] / "." 1*DIGIT

A ratio denotes width divided by height; 1920x1080 and 16:9 denote the same ratio, 16/9. A bare number denotes the ratio itself (1.5). Both numbers of a ratio MUST be greater than zero. A ratio whose quotient W/H, or a bare number, is greater than 100 or less than 0.01 is invalid. square is 1:1. landscape and portrait express intent (wider than tall, taller than wide) without a number; renderers SHOULD treat them as 4:3 and 3:4 unless they have a different default for the model. W:H with integers is the canonical spelling for authoring tools to suggest.

Renderers use the nearest ratio they support and report adjusted-value when the result differs materially from the request. The relationship between aspect and an original image is defined in § 9.3 Size and aspect.

8.1.4. mode

One of generate, transform, keep, matched ASCII case-insensitively. Semantics in § 9.1 Modes and the original image. Absent or invalid: generate.

8.1.5. strength

strength = number [ OWS "%" ]

A number from 0 to 1 inclusive, or a percentage from 0% to 100% which is divided by 100 (60% is 0.6). A value outside the range is clamped to it with adjusted-value. 0 means the source is unchanged, 1 means the source constrains nothing. Renderers map this linearly onto the model’s own control; a control that measures image influence rather than departure takes 1 − strength. strength has an effect only in transform mode; validators report strength-without-transform otherwise.

8.1.6. seed

One to twenty ASCII digits. Renderers pass the value through when it lies within the model’s accepted range and otherwise reduce it deterministically (for example modulo the range) with adjusted-value. A seed promises nothing across renderers, model versions or hardware; a renderer that cannot honour it reports unsupported-parameter, and one that runs a non-deterministic mode reports seed-nondeterministic. Authors MUST write seeds above 253 as strings in the JSON form (§ 6.3 Reading).

8.1.7. source

A URL, absolute or relative to the carrier’s base URL, or a data: URL, resolving to a raster image of type image/png, image/jpeg, image/webp, image/gif or image/avif. Other types, including SVG, HTML and image/jai, are invalid (invalid-image): a document never contains another document as an input.

An interpreter checks a source value without fetching it. A data: value is invalid when the data: URL processor returns failure or the essence of its MIME type is not one of the types above. Any other value that begins with a URL scheme (an ASCII letter followed by ASCII letters, digits, +, - or ., then :) is invalid unless the scheme is http or https, ASCII case-insensitively. An invalid source is reported with invalid-image and treated as absent, so it never also yields ignored-value. A valid value’s type is known only when it is fetched; one that cannot be fetched, or does not decode as one of these types, is not readable (§ 13.2 Permitted inputs).

A valid source is used only in transform mode (otherwise ignored-value). When present it takes precedence over the carrier’s original image. A source URL is fetched only with the privileges of the document that carries it, and only after the consent that the render itself requires (§ 13.2 Permitted inputs). Documents intended for text channels SHOULD reference a URL rather than embed bytes.

8.1.8. preview

A data: URL of a raster image of one of the types listed for source, RECOMMENDED at most 64 pixels on the long side and at most 1 KiB. A viewer shows it, scaled to the layout box, while a render is pending or when no original image exists. It is never an input to generation and never fetched from a network. Other values are ignored with unsupported-preview.

A 4×4 to 8×8 pixel lossy WebP is about 80 to 130 bytes, or 110 to 180 characters of data: URL, and shows the image’s colour layout. Hash-based placeholders such as ThumbHash [THUMBHASH] and BlurHash [BLURHASH] are three to four times smaller but need decoder code; a later edition might admit them as prefixed values without affecting this edition’s viewers.

8.1.9. animate

true, yes, 1, on (true) or false, no, 0, off (false), matched ASCII case-insensitively. True requests a looping animated image resource (GIF, APNG, animated WebP or animated AVIF) of renderer-chosen length that is displayable wherever a still image is; video containers are not JAI render results. A renderer that cannot produce one renders a still frame and reports unsupported-parameter. Viewer obligations for motion are in § 11.5.2 Accessibility.

8.2. Model matching

A renderer, or a viewer choosing among configured renderers, resolves the model values as follows. Only models the user has configured and enabled take part (§ 13.3 Selectors, never locators).

  1. For each model value v in document order:

    1. If v matches, ASCII case-insensitively, the identifier or an alias of an eligible model, select that model.

    2. Otherwise, unless the renderer pins versions: if the last /-separated segment of v contains : or @ after its first code point, let v′ be v with the text from the last such : or @ to the end removed. If v′ matches as in step 1.1, select that model and report version-ignored.

    3. Otherwise, let the words of a string be its maximal runs of ASCII letters and digits, ASCII-lowercased. A known family occurs in a list of words when the words of its name, or of one of its spellings, appear there as a contiguous run, and it has at least one eligible model. Search the words of the last /-separated segment of v; if no family occurs there, search the words of the whole of v. If several families occur in the same search, choose the one whose run starts earliest; at the same start, the one with the longer run; then the one the renderer lists first. Select that family’s default model and report substituted-model.

  2. If no value selected a model, including when the document has no model value, use the user’s default model and report no-matching-model.

  3. A model is eligible if it is configured and enabled and, in transform mode with an input image, accepts an image input. Step 2 uses the user’s default model even when it is not eligible; in transform mode with an input image it is then rendered as generate with mode-degraded (§ 9.1 Modes and the original image).

The known families are the families the renderer lists, in its order, followed by the family of every configured model not already listed, in configuration order; a family’s name is always one of its spellings. The default model of a family is the user’s default model if it is eligible and belongs to that family, and otherwise the first eligible configured model of that family.

With families flux (spellings flux) and sdxl (sdxl, stable diffusion xl): black-forest-labs/FLUX.1-dev → the words of the last segment are flux, 1, dev → family flux. stable-diffusion-xl-base-1.0 → stable, diffusion, xl, … → family sdxl. https://example.org/flux-schnell → last segment flux-schnell → flux (and a validator warns that the value looks like a URL). stability-ai/sdxl:7762fd07 → no exact match; without the version :7762fd07 it is the exact id stability-ai/sdxl (version-ignored). replicate:stability-ai/sdxl:7762fd07 → no exact match with or without the version; the words of the last segment, sdxl and 7762fd07, give family sdxl (substituted-model). replicate:flux → the segment replicate:flux without :flux matches nothing, so nothing is removed; the words replicate, flux give family flux.

Appendix Appendix B. Model family names lists family names informatively. There is no registry of models: a renderer advertises what it knows, and the user configures what the renderer may use.

8.3. Per-model refinements

A document can carry a core description that every model receives, plus refinements for particular models in qualified keys:

jai:A girl holding a red balloon in a park at dusk;prompt/flux=The balloon floats high above her;prompt/imagen=The balloon is glossy;model=flux;model=imagen

The selected model is the model that § 8.2 Model matching selects, or the model the user chose when a viewer lets the user override the document’s preference. Once a model is selected, an interpreter forms the values it uses for prompt, style and avoid: the core values of the key, followed by the values of every qualified key with that name whose target matches the selected model, in ascending code point order of the key. A target matches when it equals, ASCII case-insensitively, the model’s family name, its identifier or one of its aliases. The description given to the model is formed from these values.

Qualified keys never select a model: model remains the ordered preference list, like a font stack, and the core values are the fallback that every model receives. Qualified keys whose target does not match the selected model are unused, without a diagnostic.

Because refinements follow the core, a model that reads only the start of its input, such as one with a 77-token text encoder, may never see them. Authors who refine for such a model keep the core short, with the essentials first.

An image encoder typically evolves the core first and then adds refinements for the models in its preference list.

8.4. Never document intent

The following are settings of the user’s renderer, never parameters of a document, and renderers MUST NOT let any key, standard or vendor, set them: output format, number of candidates, quality or speed tier, resolution beyond the layout box, safety and moderation settings, provider endpoint, credentials, and anything that affects billing. See § 13.4 Parameter forwarding.

9. Rendering

A renderer turns a document into an image. This section defines what every renderer MUST do with the interpreted parameters and what it MUST report; how it produces pixels is outside this specification.

Renderer checklist (informative):

  1. compute the interpreted parameters (§ 8.1 Interpretation);

  2. select the model (§ 8.2 Model matching);

  3. resolve the mode and the input image (§ 9.1 Modes and the original image, § 13.2 Permitted inputs);

  4. assemble the model input (§ 9.2 Assembling the model input);

  5. forward vendor keys only through the mapping (§ 13.4 Parameter forwarding);

  6. size the output (§ 9.3 Size and aspect);

  7. report every interpreter and renderer code (§ 9.4 Renderer diagnostics);

  8. handle failure (§ 9.5 Failure).

9.1. Modes and the original image

The original image is the carrier’s own image (an <img> element’s currentSrc at render time) or, when present, the image named by source. mode states how the document relates to it; a viewer’s settings always take precedence over mode (§ 11.5 Viewer requirements).

generate (default)

Render from the description alone. A viewer shows the original image, or the preview, until the render is ready, then replaces it, and falls back to the original on failure.

transform

As generate, but the original image is an input to generation, governed by strength. Model matching considers only models that accept an image input; if none of the preferred models does, the renderer’s default model is used when it accepts an image, and otherwise the document is rendered as generate with mode-degraded. If there is no original image and no readable source (§ 13.2 Permitted inputs), the document is rendered as generate with original-not-readable. As in generate, the description states what the resulting image shows, including what is kept from the original; it is not an edit instruction. It may be absent when only style applies. Because a transform document is rendered as generate when the original cannot be read, a description that names its subject still produces a fitting image.

keep

The original image is the intended image; the description is information about it. A viewer MUST NOT render a keep document automatically, even under an automatic site setting; a render the user requests is a generate render unless the user asks for a transformation.

A renderer whose model expects an instruction can wrap the description (for example "Make this image show: …"); the text sent is reported as for any render (§ 9.2 Assembling the model input).

9.2. Assembling the model input

The reference renderer assembles the prompt values joined with line feeds, then the style values as a comma-separated list, then, only when folding, a final line listing what to avoid. On models with a short token window, style terms belong early. Provider-side prompt rewriting is a user setting of the renderer, not a document matter.

9.3. Size and aspect

The governing ratio of an element is the aspect ratio of its original image if it has one, and otherwise the ratio of an author-sized layout box (both width and height attributes, or a CSS aspect-ratio) if there is one. A standalone document (a .jai file opened on its own) has no governing ratio.

aspect therefore shapes renders of unsized elements and standalone files, and records the author’s intent everywhere. On a page, the shape the author gave the element wins: authors who set both make them agree.

Renderers snap to the nearest ratio they support (§ 8.1.3 aspect). The layout box always governs display: when the render’s ratio still differs from the box (for example after snapping), the viewer honours the page’s object-fit and object-position; if the page sets neither, it crops the render to cover the box, centred. A viewer MUST NOT stretch a render to a box of a different ratio.

Viewers SHOULD request a render no smaller than the element’s displayed CSS size multiplied by the device pixel ratio, within the user’s cost policy, and never larger than the user’s configured maximum.

9.4. Renderer diagnostics

A renderer MUST NOT refuse to render a valid document because of the value of any standard key, and MUST report, with every code of class renderer or interpreter in § 14 Diagnostics, every standard key it did not honour as written. A viewer MUST make those reports available to the user.

9.5. Failure

A render fails when it produces no usable image: a provider or network error, a refusal or content filter (including a blank or black frame), a timeout, or a cancellation. On failure a viewer MUST restore the original image, or in the absence of one the preview or the description, MUST NOT leave a permanent placeholder, and MUST NOT retry with an altered description. A refusal is a normal outcome: viewers MUST NOT rewrite the description, or let a document cause it to be rewritten, to evade a refusal.

10. Encoding images

This section is informative.

An image encoder produces a document from an existing image. It is a tool, not a conformance class: its output is an ordinary document, and the encoder class above only concerns the file form. Nothing here constrains how an image encoder works; the section describes the approach that the compression view in § 1.1.1 A description is compression suggests, and the conventions that make encoded documents easy to recognise and reuse.

10.1. Analysis by synthesis

An image encoder searches for the description whose renders look most like the original, using the target models themselves as the decoder:

  1. Describe. A vision-capable model writes a first description of the original, following § 15 Writing JAI by hand: medium first, subject and counts, composition, light and colour, literal text in quotes.

  2. Render. Each candidate description is rendered with every target model, with fixed seeds so that differences between candidates come from the text, not from noise.

  3. Compare. Each render is scored against the original. Useful measures are semantic (cosine similarity of image embeddings such as CLIP), perceptual (colour and layout distance between small thumbnails), and a vision model’s list of the visible differences. The fitness of a candidate is its mean score across the target models.

  4. Revise. The best candidates are kept, and new ones are written from them by addressing the listed differences, while keeping what already matches.

  5. Refine per model. Once the core description stops improving, the encoder looks at models that lag behind and tries short per-model refinements for them, keeping a refinement only when it raises that model’s score.

  6. Stop. On a budget, a time limit, or when scores reach a plateau, the encoder returns the best document found so far.

This is the loop a CELP speech coder runs, with a description in place of codebook indices and a generative model in place of the synthesis filter. The result is a code tuned to the decoders the author chose. Other models still receive the core description, which remains a faithful general description of the image.

The quality of an encoded document is bounded by its decoders: a model that cannot draw hands, legible text or exact counts will not do so because the description is better. Scores measure resemblance, not truth; an encoder that optimises an embedding score alone can drift towards text that games the metric, which is why the comparison step also uses a reader of the images.

10.2. Output conventions

An image encoder is expected to produce a document that a person could have written by hand, so that it stays portable and editable:

11. HTML and URL integration

11.1. Carriers

A document reaches an <img> element through one of four carriers. A viewer examines them in the order below and takes the first one found; the same order is the recommended order for authors.

carrier 1: <picture> with <source type="image/jai">
<picture>
  <source type="image/jai" srcset="castle.jai">
  <img src="castle.png" alt="A magical castle at dusk">
</picture>
This is HTML’s own format negotiation: a browser that does not support image/jai skips the <source> without fetching it and shows the raster [HTML]. src and alt stay untouched, the .jai is fetched only when a viewer wants to render, and a browser that one day decodes image/jai natively needs no markup change. The srcset may name a .jai resource or hold a data:image/jai URL.
carrier 2: the data-jai attribute
<img src="castle.png" alt="A magical castle at dusk"
     data-jai="A magical castle at dusk;style=anime">
The attribute holds the text form; the jai: prefix is optional. It is never read as the JSON form: a JSON object in data-jai is prose. This is the inline form for markup that cannot be restructured.
HTML intends data-* attributes for a page’s own scripts and recommends type negotiation for extensions shared across tools, which is why carrier 1 is preferred. Carrier 2 is kept because it survives most sanitisers and needs no extra element.
carrier 3: src is a data:image/jai URL
<img src="data:image/jai,A%20magical%20castle%20at%20dusk;style=anime"
     alt="A magical castle at dusk" width="800" height="600">
The pure form: the image is the description and there are no fallback pixels. In every current browser this is an unavailable image whose alt text is what the reader sees; see § 11.1.2 Requirements for carrier 3.
carrier 4: alt beginning with jai: (deprecated)
<img src="castle.png" alt="jai:A magical castle at dusk;style=anime">
Deprecated. The accessible name of the image becomes the syntax, which assistive technology announces, and every parameter is read aloud. Documents SHOULD NOT use this carrier where any other is available; validators report alt-carrier. It is recognised because it is the only carrier that Markdown and strict sanitisers let through, and in those contexts a document should be the prefix and a short description with few or no parameters. A document MUST NOT place a data: URL (preview, source or any other value) in an alt carrier (alt-carrier-payload). A viewer MUST NOT render automatically from this carrier; it MAY offer rendering on explicit per-image request.

Carriers 1 and 3 are HTML’s own extension points and are the only carriers a user agent that decodes image/jai natively implements. Carriers 2 and 4 are defined for scripts and extensions acting for a user on a page that opted in; a native user agent ignores them unless the user enables such a viewer.

An alt attribute that does not begin, after leading JAI whitespace, with jai: is never a JAI document. A jai: string in any URL slot (src, href, srcset, CSS url()) is not a carrier: jai: is a text prefix and this specification defines no jai: URL scheme (jai-scheme-url).

11.1.1. Discovery

A viewer runs discovery over the <img> elements of a document in tree order, descending into open shadow roots and skipping closed ones, and re-runs it on the mutations HTML considers relevant for source selection (changes to src, srcset, sizes, media, type, and to the children of a <picture>), as well as on data-jai and alt changes. Discovery MUST NOT depend on the image having been fetched or having failed: lazily loaded images fire no events until they approach the viewport.

For an <img> element:

  1. If the element’s parent is a <picture>: consider each <source> that precedes the <img> among the parent’s children, in tree order, whose type attribute has the essence image/jai and whose media attribute is absent or matches the environment. Parse its srcset with HTML’s parse a srcset attribute algorithm [HTML]; a candidate whose descriptors are invalid is dropped exactly as HTML drops it. If no candidate remains, skip the <source> (validators report srcset-unparseable). Otherwise let url be the first candidate’s URL; descriptors play no further part. If url is a data: URL and the data: URL processor [FETCH] returns failure, or returns a MIME type whose essence is not image/jai, skip the <source>. Otherwise the document is the resource at url, resolved against the element’s base URL, and no later <source> is examined. width and height on the <source> are layout hints. An implementation that cannot evaluate media queries treats every media value as matching.

  2. Otherwise, for a script or extension viewer: if the element has a data-jai attribute, the document is its value parsed with the text parsing algorithm.

  3. Otherwise, if the element’s src attribute, parsed against the base URL, is a data: URL that the data: URL processor accepts and whose MIME type essence is image/jai: the document is its body (§ 7.5 data: URLs).

  4. Otherwise, for a script or extension viewer: if the element’s alt attribute begins, after JAI whitespace, with jai: ASCII case-insensitively, the document is the attribute value parsed with the text parsing algorithm (carrier 4).

  5. Otherwise the element carries no document.

If the element’s currentSrc is the URL selected in step 1 and the element is complete with a natural width greater than zero, the user agent decodes image/jai natively and the viewer stands down.

A <source> without a type attribute is selected by the browser and yields a broken image; validators report source-missing-type. A <source> after the <img> is ignored by HTML (source-after-img). A JAI URL MUST NOT appear in the <img>’s own srcset. Authors place the image/jai source before raster sources when JAI-capable user agents should prefer it; an extension’s discovery does not depend on the order. A <source type="image/jai"> for a document with mode=keep is unusual, since a native decoder would then never show the raster (keep-in-source).

A user agent that decodes image/jai natively would select the JAI source and never load the raster original; mode then applies only with an explicit source. HTML never re-selects a source after a decode fails, so native support will need a rule that a declined or failed render re-selects the next candidate; the editors intend to raise this with the HTML Standard’s maintainers when a native implementation exists.

11.1.2. Requirements for carrier 3

An <img> whose src is a data:image/jai URL:

Authors are warned that pages without a JAI viewer show an unavailable image; that onerror handlers and lazy-loading libraries rewrite a broken image’s src (viewers capture the carrier at discovery); that a Content Security Policy must list data: in img-src; and that carrier 1 is preferred whenever fallback pixels exist. The inline pure form with a fallback is carrier 1 with a data: URL in srcset:

<picture>
  <source type="image/jai" srcset="data:image/jai,A%20magical%20castle;style=anime">
  <img src="castle.png" alt="A magical castle">
</picture>

11.1.3. Serving .jai resources

This section is informative. See § 7.3 Media type and file extension for the server requirements. A script on another origin can read a .jai resource only when it is served with CORS headers; browser extensions with host permissions fetch it from their privileged context but are still bound by § 13.2 Permitted inputs for any image input. Appendix Appendix D. Serving .jai files gives server mappings.

11.2. Transport decoding

Exactly one transport decoding is applied per carrier, before the decoding algorithm or the text parsing algorithm runs:

No second percent-decoding is ever applied. Implementations MUST NOT use decoders that throw on malformed input (decodeURIComponent) in a transport step. Transport decoding never changes JAI syntax: %3B and %3D become ; and = and parse as delimiters.

Reading the src attribute and reading the element’s src property give the same data: body by construction, because the data: URL processor starts from the parsed URL either way.

11.3. Query forms

Two conventions carry a document in a URL’s query, for playgrounds, gateways and links in text channels.

whole-query form

https://example.org/play?prompt=A%20magical%20castle;style=anime

Parse the URL with the URL parser [URL]. If its query is null, the URL carries no document. Otherwise percent-decode the query once as in § 11.2 Transport decoding, decode the bytes with UTF-8 decode without BOM [ENCODING] (ill-formed sequences become U+FFFD), and run the text parsing algorithm on the result; an empty query is the empty document. The JSON test of § 7.4 Decoding does not apply. + is a literal plus sign and & is ordinary text; the URL carries no other parameters. Generators MUST percent-encode space as %20 (never +), %, # and ASCII whitespace, and MAY leave ;, =, +, &, :, / and non-ASCII unencoded. Form serialisers and parsers (URLSearchParams, <form method=get>) MUST NOT be used to build or read this form.

parameter form

https://example.org/render?jai=A%20magical%20castle%3Bstyle%3Danime&w=800

A query parameter named jai, in application/x-www-form-urlencoded [URL], whose value is a text-form document. Parse the URL with the URL parser; if its query is null there is no document. Otherwise parse the query with the application/x-www-form-urlencoded parser; the value of the first pair named jai is run through the text parsing algorithm (an empty value is the empty document). With no such pair there is no document. Raw tabs and newlines have already been removed by the URL parser. It is read by any form parser, so + is a space and & separates parameters; generators encode ;, =, &, +, #, % and whitespace in the value. This form coexists with other parameters and is the convention for a gateway: a server that renders a document to pixels so that <img src="https://gateway/…?jai=…"> displays everywhere today. Hosting a gateway is outside this specification; a gateway that stores or publishes prompts or watermarks output must say so to its users. A JAI-aware viewer MAY, with the user’s consent, decode the parameter locally instead of fetching the gateway.

11.4. Other host formats

This section is informative.

11.4.1. Carriers by context

This section is informative. Sanitisers differ, and the carrier an author can use depends on the pipeline:

Context Usable carriers
HTML the author controls all; prefer carrier 1
User content through DOMPurify defaults carriers 2 and 3, and carrier 1 with an http(s) or relative .jai URL (data: URLs in srcset are removed)
User content through GitHub-style allow-list sanitisers carrier 1 as raw HTML (<picture>, <source>, srcset, type survive; data-* and data: src do not), else carrier 4
CommonMark image syntax carrier 4 only, or a gateway URL
Chat, documentation, email text the text form with its prefix

11.5. Viewer requirements

A viewer decides whether, when and how documents are rendered. The user always wins: user settings (never render, ask, render on request, render automatically for a site) override mode and every other author preference, except that keep is never rendered without a user request (§ 9.1 Modes and the original image), and the consent, budget and disclosure requirements of § 13.5 Consent, budgets and cancellation apply to every render.

A viewer MUST NOT present a render as saving data or bandwidth when the original image was fetched, and SHOULD NOT state any saving unless it avoided fetching the original.

11.5.1. Replacement

A viewer that replaces an image with a render:

  1. MUST make the replacement effective under HTML’s image source selection and MUST verify it (the load event and currentSrc). Setting src alone is a no-op when the element has a srcset or a preceding matching <source>. RECOMMENDED: inside a <picture>, insert a viewer-owned <source> whose srcset is the rendered image as the first child and remove it to restore; otherwise set the element’s srcset, recording the previous value.

  2. MUST keep the layout box: the element’s width and height or CSS aspect-ratio, else the original’s ratio.

  3. MUST NOT alter id, class, event listeners, or link ancestors, and MUST keep the element in place where possible; a replacement element carries role="img" and the same accessible name and description.

  4. MUST record every state it changes and restore all of it on request and on failure, and MUST let the user compare the original and the render.

  5. MUST NOT replace the same element more than once per identity per page load, and MUST NOT re-render on resize or scroll. If the page changes src, srcset or the <picture> children of a replaced element, the viewer treats the element as new and releases its saved state.

  6. SHOULD NOT, under an automatic setting, start rendering before the user agent would fetch the image (for loading=lazy, not before it is near the viewport), and MUST NOT render elements that are not displayed.

  7. MUST obtain the rendered bytes itself and insert them from local data; it MUST NOT point a page element at a provider URL (which would leak the page through Referer and reveal the provider).

  8. MUST confirm that a replacement loaded (load, natural width greater than zero) and MUST NOT retry the same URL. Depending on the engine and on how the viewer is implemented, replacement loads may be governed by the page’s Content Security Policy [CSP3], under which 'self' never matches blob: and * never matches data:. (Chrome currently exempts loads initiated by an extension’s content script from the page’s policy; other engines and page scripts are not exempt.) When a replacement does not load, the viewer either restores the original or presents the render without a page-initiated fetch (for example an ImageBitmap painted into a canvas sized to the layout box with role="img" and the original accessible name) or in viewer-owned UI. Viewers SHOULD prefer a data: URL of the rendered bytes over a blob: URL, which more policies permit.

  9. Viewer UI injected into a page is subject to the page’s style-src and script-src; a shadow root with constructed stylesheets avoids both.

11.5.2. Accessibility

  1. A viewer MUST keep a human-written alt unchanged, including alt="". If the alt carried the document (carrier 4) or is absent, the accessible name becomes the first prompt value with control characters removed, truncated at a grapheme cluster boundary at about 250 code points, with the full text available in the viewer’s own UI; when the document has no prompt, a localised generic name. Keys, URLs and data: payloads never appear in an accessible name.

  2. A viewer SHOULD expose to assistive technology what it shows sighted users about the image’s state: that it was rendered by an AI model from a description, that it may differ from the publisher’s image, and that the original is available. aria-description (or aria-describedby referring to an element the viewer owns) carries this; title does not. aria-busy is set while a render is pending.

  3. Per-image controls MUST be keyboard-operable and named, never hover-only.

  4. Under prefers-reduced-motion the swap SHOULD happen without transitions. Individual replacements are not announced to assistive technology by default.

  5. For animate: under prefers-reduced-motion a viewer shows a still frame until the user starts the animation; it shows the first frame until the user starts playback unless the user has opted into autoplay for the site; it provides a keyboard-operable control to pause, stop or hide animation longer than five seconds [WCAG22]; renderers SHOULD instruct the model to avoid flashing; viewers MAY refuse to play animation they detect as flashing.

  6. The viewer’s own UI (consent, provider configuration, per-image controls, diagnostics) MUST be keyboard-operable and expose names, roles and states.

  7. A JAI-aware user agent MAY, without rendering, expose the description as the original image’s accessible description; it MUST NOT replace a human-written alt with it.

Generated imagery carries no authority: logos, text and interface elements inside a render are model output. Viewers mark rendered images as such in viewer-owned UI and always offer the original.

The page’s own accessibility conformance is unaffected by what a user’s agent does at the user’s request; a conforming viewer is a user agent in the sense of the User Agent Accessibility Guidelines.

11.5.3. Personalisation

At the user’s request, a viewer MAY render a document with transformations the user chose: for example a preferred style, a sensitivity or accessibility adjustment, the text inside the image translated into the user’s language, or a mood. A personalised render:

  1. MUST be marked as personalised, in viewer-owned UI and to assistive technology, alongside the AI-rendered state (§ 11.5.2 Accessibility);

  2. MUST leave the document unchanged, and the viewer MUST keep a render without personalisation available to the user;

  3. is reported like any render: the text sent to the model includes the personalisation (§ 9.2 Assembling the model input).

Personalisation is a user setting. A document or a page MUST NOT be able to request or trigger it, and a viewer MUST NOT add a personalisation in order to get past a refusal (§ 9.5 Failure).

12. Extensibility

13. Security and privacy considerations

Decoding a JAI document has side effects that no other image format has: it transmits the document, and possibly an input image, to a renderer; the render is performed under the user’s provider account and is attributable to the user; and it usually costs money. Every requirement in this section follows from that.

13.1. Untrusted input and bounds

All JAI input is untrusted. Parsing, reading and decoding are total (§ 2.2 Determinism): no input causes an exception, a crash, a hang, or unbounded resource use.

13.2. Permitted inputs

A viewer MUST NOT use as an input to rendering any image data that the embedding document could not itself read. In a browser that is exactly the data that would not taint a canvas: same-origin images, data: images and blob: images of the page’s origin, and cross-origin images loaded with CORS that succeeded for the document’s origin. Input pixels are obtained with the document’s own privileges (a content script’s canvas, or a fetch applying the document’s CORS and credential rules), never from a privileged context that carries the user’s cookies. When the implicit original is not readable, transform behaves as generate with original-not-readable unless a readable source is present. Publishers who want transform on cross-origin originals serve them with CORS headers and crossorigin="anonymous".

A source URL is fetched only with the carrier document’s privileges and only after the consent the render requires. Renders that transmit an input image SHOULD require a per-image confirmation that shows the image and names its origin.

13.3. Selectors, never locators

model is a selector: a viewer matches its values only against renderers and models the user has configured and enabled. A viewer MUST NOT dispatch to a model, package, endpoint or workflow because a document names it, even inside a configured provider’s namespace, and MUST NOT download, install, load or execute any artefact in response to a model value or to any other key, standard or vendor, that names weights, LoRAs, workflows, graphs or packages. Unmatched values use the user’s default renderer with no-matching-model and are shown to the user as text, never as a link. Viewers SHOULD let the user mark a renderer as available only on explicit request.

13.4. Parameter forwarding

A document, through any standard or vendor key, MUST NOT be able to cause a viewer or renderer to fetch a URL, load weights, workflows or code, select or alter the endpoint, credentials, headers, webhooks or callbacks, change safety or moderation settings, or exceed the user’s configured cost policy (candidate count, steps, resolution, duration).

A renderer MUST forward document parameters to a provider only through a mapping it controls: standard keys through its own semantics, and vendor keys only into the provider’s model-input namespace, after validating each value against the provider’s published input schema and the user’s limits, and only when the key is not on the renderer’s per-provider deny-list of inputs that name artefacts, carry credentials, or affect safety, output count or billing. A renderer MUST ignore a key it will not forward and report parameter-not-allowed. "Preserved when serialising" (§ 12 Extensibility) binds serialisers, not renderers.

For a hosted Flux-family model the deny-list includes at least the inputs that disable the safety checker, set the number of outputs, name LoRA or other weight files, or carry third-party API tokens.

13.6. What leaves the browser

A viewer sends a provider only the parameters its renderer maps and the input image bytes it decoded and re-encoded locally. It MUST NOT send the page URL, title, referrer, cookies, identity or other page content, and SHOULD send image bytes rather than URLs. The viewer’s disclosure names the provider and links its retention terms. Any in-page UI, replacement or .jai fetch reveals to the page that the user runs a viewer.

Credentials of the reference viewer are stored extension-locally, used only from its background context, never exposed to pages or content scripts, requested only in viewer UI, sent only to the configured endpoint, and redacted from logs; the background context accepts a provider endpoint, allow-listed parameters and image bytes, never arbitrary URLs.

13.7. Caches

A render cache keyed by identity is a cross-site history oracle. A cached render is subject to the same per-site consent and the same user action as an uncached one, and viewers SHOULD prevent documents from distinguishing cross-site cache hits, by keying the cache on (identity, renderer configuration, top-level site) or by applying comparable delay.

13.8. Viewer UI hygiene

Viewer UI MUST render every document value as text, never as markup; SHOULD isolate bidirectional text (unicode-bidi: plaintext or <bdi>); MUST NOT silently truncate (prompt, avoid, style and model are fully available); SHOULD visualise or warn on control and invisible formatting characters; MUST show the top-level site; and MUST display the configured renderer that matched, never the document’s model string, as the thing that will run. The same rules apply to playgrounds and validators, which are cross-site scripting surfaces for preview values and generated HTML snippets.

13.9. Guidance for publishers

Sites that accept user markup and do not intend to publish JAI SHOULD strip data-jai, alt values beginning with jai: in any case, data:image/jai URLs in src and srcset, and <source type="image/jai">. DOMPurify’s defaults keep all of them; strict allow-list sanitisers remove all but alt. A .jai referenced by URL is fetched per viewer at render time and may change after review; publishers who need stability inline the document or record its identity. Injection through templated prose (§ 5.3 Escaping arbitrary text) can truncate or override a document but, because of the rest of this section, can neither select a renderer nor spend beyond the user’s limits.

13.10. Provenance

Renderers that write files SHOULD set the IPTC digitalSourceType to trainedAlgorithmicMedia (compositeWithTrainedAlgorithmicMedia for transform) [IPTC-PHOTOMETADATA] and MUST NOT strip provider-applied provenance such as C2PA manifests [C2PA] or watermarks when re-encoding. Viewers mark rendered images as generated (§ 11.5.2 Accessibility).

14. Diagnostics

A diagnostic is a code from the table below, optionally with the key it concerns, and with any human-readable message the implementation chooses. Codes are stable identifiers; messages are not.

The Key column names the key a diagnostic concerns. When it names one, an implementation MUST attach it to the diagnostic. Fixtures write a diagnostic as code or code:key; a fixture that writes code alone does not constrain the key.

Code Class Key Condition
empty-segment parser none An interior empty or whitespace-only segment was ignored.
empty-value parser the key of the assignment or member (ASCII-lowercased; prompt for prose) A value was empty after normalisation and was dropped.
reserved-key parser jai An assignment to the reserved key jai was dropped.
invalid-key parser none (MAY carry the member name as written) A JSON member name is not a valid key; the member was dropped.
missing-marker parser none JSON object without a jai member, read in JAI context.
unsupported-version parser none The jai member is an integer other than 1.
invalid-marker parser none The jai member is not an integer.
number-value parser the key of the assignment or member (ASCII-lowercased; prompt for prose) A JSON number or boolean was converted to a string.
duplicate-member parser jai A member name other than jai lowercases to jai; the member was dropped. (Validators MAY also report an exact repetition of a member name with this code.)
duplicate-key parser the key of the assignment or member (ASCII-lowercased; prompt for prose) Two JSON member names collided after ASCII lowercasing; values were concatenated.
unsupported-member parser the key of the assignment or member (ASCII-lowercased; prompt for prose) A JSON object or nested array was dropped (reserved for future revisions).
invalid-json parser none The JSON form could not be parsed; the document is empty.
malformed-container parser none The gzip member was malformed, followed by bytes, over the bound, or did not contain JSON; the document is empty.
undecodable parser none The bytes are not well-formed UTF-8 or exceed the text bound; the document is empty.
first-segment-assignment validator the key concerned The first segment was an assignment to an unknown key and the document has no prompt.
invalid-qualifier validator the key concerned A qualified key whose name is not prompt, style or avoid.
unknown-key validator the key concerned An undotted key that this specification does not define.
continuation-into-single-valued-key validator the key concerned Prose continued the value of aspect, mode, strength, seed or animate (not source or preview, whose data: URLs contain semicolons; see split-data-uri).
no-prompt validator none No prompt, and the interpreted mode is generate.
bare-key validator the key concerned A prose segment is exactly a standard key; key=true was probably intended.
newline-assignment validator the key concerned A value contains a line break followed by key=.
prefix-in-prompt validator the key concerned A prompt value still begins with jai:.
stray-wrapper validator none The whole document is wrapped in quotes, brackets or a Markdown fence.
split-data-uri validator the key concerned An unknown key such as charset or name follows a source or preview value: an unescaped ; inside a data URL.
url-split validator the key concerned A URL-like value was followed by an unknown-key assignment.
confusable-delimiter validator none A full-width or Arabic semicolon is followed by what looks like key=.
confusable-equals validator the key concerned A full-width equals sign follows a key.
foreign-syntax validator none The text looks like another prompt dialect (--ar 16:9, Negative prompt:, <lora:…>).
colon-assignment validator the key concerned key: value was written where key=value was probably intended.
quoted-value validator the key concerned A value was wrapped in quotation marks.
percent-encoded-text validator the key concerned A non-URL value contains %XX sequences and no %25.
control-character validator the key concerned A value contains a control character other than TAB and LF.
invisible-char validator the key concerned A value contains U+200B to U+200D, U+2060 or a non-initial U+FEFF.
bidi-control validator the key concerned A value contains U+202A to U+202E or U+2066 to U+2069.
nonstandard-value validator the key concerned A valid value in a non-canonical spelling (16x9); the canonical spelling is offered.
invalid-value validator, interpreter, renderer the key concerned A standard key’s value does not match its grammar and was ignored.
negated-avoid validator the key concerned An avoid value begins with a negation.
strength-without-transform validator the key concerned strength without mode=transform.
repeated-single validator, interpreter, renderer the key concerned A single-valued key had several values; the last was used.
model-looks-like-locator validator the key concerned A model value looks like a URL or a path.
large-payload validator the key concerned (none for the document-size condition) A value exceeds 8 KiB or the document exceeds 64 KiB.
non-canonical-encoding validator none A .jai file, or an image/jai resource fetched by URL (not a data: URL), is not in the file form.
unencoded-url-text validator none A URL-slot value contains whitespace, # or a bare %.
srcset-unparseable validator none The srcset of a <source type="image/jai"> yields no candidate under HTML’s srcset parsing (for example because of an unencoded space).
jai-scheme-url validator none A jai: string in a URL slot.
alt-carrier validator none Carrier 4 is used.
alt-carrier-payload validator none A data: URL inside an alt carrier.
missing-size validator none A carrier-3 image without width/height or aspect-ratio.
attribute-split validator none An element carrying a JAI carrier has an attribute whose name contains ", ', ; or =: an unescaped quote probably ended the carrier attribute early.
data-url-missing-comma validator none A data:image/jai URL in a carrier has no ,.
data-url-parameter validator none A data:image/jai URL has a media-type parameter other than base64 or charset.
source-missing-type validator none A <source> naming a .jai or data:image/jai URL has no type.
source-after-img validator none A JAI <source> follows the <img>.
keep-in-source validator none A <source type="image/jai"> carries a mode=keep document.
no-matching-model interpreter, renderer model No model value selected a model (or the document has none); the default was used.
substituted-model interpreter, renderer model A family match selected a model the author did not name exactly.
version-ignored interpreter, renderer model A version suffix of a model value was ignored.
mode-degraded interpreter, renderer mode transform with an input image was rendered as generate because the selected model accepts no image.
original-not-readable renderer mode transform had no readable input image. Decided when inputs are fetched; interpreters do not emit it.
unsupported-parameter renderer the standard key concerned A standard key could not be honoured by the model and was omitted.
folded-into-prompt renderer avoid avoid values were folded into the prompt text.
adjusted-value interpreter, renderer the standard key concerned A value was clamped, snapped, reduced or shortened.
ignored-value interpreter, renderer the standard key concerned A value had no effect in this context (aspect with an original, source outside transform).
seed-nondeterministic renderer seed The seed was passed but the mode is not deterministic.
parameter-not-allowed renderer the vendor key refused A vendor key was refused by the forwarding rules.
value-truncated viewer the standard key concerned A value was shortened before transmission.
unsupported-preview interpreter, renderer preview A preview value is not an allowed raster data: URL.
invalid-image interpreter, renderer source A source value is not an allowed raster image.

14.1. Conformance fixtures

Fixtures live under tests/<class>/<class>.json in the specification repository. Each file is a JSON object with class, edition and cases.

Every case has id (unique across the suite), since and rationale. A document is an object mapping each key to an array of strings. A diagnostic is written code or code:key (§ 14 Diagnostics). diagnostics is compared as an exact multiset. advisories lists validator codes that an implementation with a validator MUST emit; other codes are allowed. no_advisories lists validator codes that it MUST NOT emit.

parse

input (string) → document, diagnostics; optional advisories, no_advisories.

serialize

document → text (canonical text), json (canonical JSON), identity (lowercase hex SHA-256); optional bare (bare serialisation) and url (canonical URL form, § 7.5 data: URLs); each field is compared when present.

roundtrip

document → the document survives, and serialisation is a fixed point of, each of: the canonical text parsed with the text parsing algorithm; the bare serialisation and the canonical URL form decoded with § 7.4 Decoding in a JAI context; the canonical JSON read with the JSON reading algorithm; the file form decoded with § 7.4 Decoding.

json

input (JSON text) and context (boolean, default true: whether the text is in a JAI context) → document, diagnostics.

decode

bytes (base64) and optional bound (the decompression bound in bytes, default 16 MiB) → document, diagnostics; optional advisories, no_advisories.

url

url (string) and kind → document and diagnostics, or discovered: false when the URL carries no document. kind data applies § 7.5 data: URLs; query and param apply the two forms of § 11.3 Query forms.

html

html (a fragment parsed as HTML’s fragment parsing algorithm does, with declarative shadow roots attached; base URL https://example.test/page/; every media value matches) → discovered, one entry per <img> that carries a document, in the order of § 11.1.1 Discovery: carrier (1 to 4), and either document and diagnostics, or url, the candidate URL as written in srcset, compared after resolving it against the base URL. Optional advisories and no_advisories apply to all carriers in the fragment.

interpret

document; optional catalog, the configured models in configuration order, each {id, aliases, family, image_input, default} (family may be null; default: true marks the user’s default model, otherwise the first entry is the default); optional families, the renderer’s family list, mapping each family name to its spellings in order; optional has_original (boolean, default false: whether the carrier offers an original image); optional governing_ratio (number: the element’s governing ratio, absent for none); optional selected (the id of a catalog entry the user chose, which is then the selected model instead of the result of § 8.2 Model matching) → interpreted and diagnostics. interpreted is compared only for the members present: description (string, after § 8.3 Per-model refinements); style and avoid (arrays of strings, after § 8.3 Per-model refinements); aspect (W/H as a number, square as 1, or the string landscape or portrait); mode (string, the interpreted value of the mode key); strength (number); seed, source, preview (string); animate (boolean); model (the id of the selected catalog entry); ratio (the ratio to render at under § 9.3 Size and aspect, W/H as a number; null when the preview or the renderer’s default decides). null means absent or invalid. diagnostics is the exact multiset of interpreter codes.

Fixtures are append-only from the first tagged edition: a changed expectation is a new case id with an erratum, never a silent edit.

15. Writing JAI by hand

This section is informative.

  1. Write the description first, as you would describe the picture to someone on the phone: what is in it, how it is arranged, the light, the colours, the medium. Ordinary punctuation, including semicolons, is fine.

  2. Add settings after a semicolon, each as name=value: ;style=anime, ;aspect=16:9, ;avoid=text. Repeat style and avoid for several items rather than listing them in one value.

  3. Put jai: in front when the place you are writing in does not already say it is JAI (a chat message, a document). On a web page, put the text in a data-jai attribute (no prefix needed) or use the <picture> form; avoid alt, which screen readers read aloud.

  4. If your description itself starts with a word followed by =, start with prompt= instead. If you need a literal ; right before something that looks like name=, write \;.

  5. For an existing photo you want restyled, add ;mode=transform and ;strength=0.6. Describe the finished picture, including what stays the same, not the change.

  6. Name a model only if you care: ;model=flux. It is a preference, not a command; the reader’s own setup decides.

  7. Check it in the playground: it shows what a reader’s software will see and offers the canonical spelling to copy.

16. Examples

This section is informative.

A minimal description, equally valid with or without the prefix:
A red bicycle leaning against a whitewashed wall, afternoon sun
One description, three styles:
jai:A lighthouse on a cliff in a storm;style=watercolor
jai:A lighthouse on a cliff in a storm;style=anime
jai:A lighthouse on a cliff in a storm;style=35mm film photo;style=grainy
Model preferences, in order, advisory:
jai:A still life of pears on pewter, Dutch golden age;model=flux;model=sdxl;aspect=4:3
Image-conditioned rendering, with the page’s own image as the input:
<picture>
  <source type="image/jai" srcset="data:image/jai,Fishing%20boats%20in%20a%20harbour%20at%20dawn,%20as%20a%20loose%20pencil%20sketch;mode=transform;strength=0.7">
  <img src="harbour.jpg" alt="Fishing boats in a harbour at dawn" crossorigin="anonymous" width="1200" height="800">
</picture>
A publisher’s photograph with a description for agents and future readers, not to be replaced automatically:
<img src="team.jpg" alt="The four founders in front of the office"
     data-jai="Four people in casual clothes standing in front of a brick office building, overcast daylight;mode=keep">
A .jai file’s content, decompressed:
{"jai":1,"prompt":["A magical castle on a hill at dusk, lanterns in the windows","Mist in the valley below"],"aspect":"16:9","avoid":["people","text"],"model":"flux","style":"storybook illustration"}
A vendor extension and a Dublin Core title, preserved by every serialiser:
jai:A brass astrolabe on velvet;comfy.sampler=euler;dc.title=Astrolabe study

Appendix A. Media type registration template

This appendix is informative until registration is pursued. It follows the form of RFC 6838 §5.6 [RFC6838].

Type name

image

Subtype name

jai

Required parameters

none

Optional parameters

none (charset has no meaning; content is always UTF-8)

Encoding considerations

binary. The file form is a gzip member; the text and JSON forms are UTF-8.

Security considerations

See § 13 Security and privacy considerations. Decoding transmits content to a renderer and is performed under the viewer’s account; decoders bound decompression; model references are selectors and are never fetched; documents are untrusted input and parsing is total.

Interoperability considerations

Three serialisations are distinguished by their leading bytes (§ 7.4 Decoding). Renderers produce different pixels for the same document by design.

Published specification

This document.

Applications that use this media type

Browsers and browser extensions, image generation tools, authoring tools, AI agents.

Fragment identifier considerations

none

Additional information

Deprecated alias: none. Magic numbers: 1F 8B (gzip), then {"jai":1 followed by , or } after decompression; or { for the uncompressed JSON form. File extension: .jai. Macintosh file type code: none.

Person and email address to contact for further information

The editors, via the specification repository.

Intended usage

COMMON

Restrictions on usage

none

Author

The editors.

Change controller

The specification repository.

Appendix B. Model family names

This appendix is informative. Family names and spellings a renderer may recognise in model values. The list implies nothing about availability.

Family Spellings
flux flux, flux-schnell, flux-dev, flux-pro, kontext
sdxl sdxl, stable diffusion xl, stable-diffusion-xl
sd3 sd3, stable diffusion 3
ideogram ideogram
recraft recraft
dall-e dall-e, dalle
gpt-image gpt image, gpt-image
imagen imagen
midjourney midjourney, mj

Appendix C. Relationship to existing formats

This appendix is informative.

Prompt metadata in rasters. Tools such as AUTOMATIC1111 and ComfyUI write their prompt and settings into PNG text chunks. An importer maps them as follows: the positive prompt → prompt; "Negative prompt" → one avoid value per comma-separated item; "Seed" → seed; "Model" or ckpt_name → model; "Denoising strength" → strength with mode=transform; "Size" → aspect (lossily). Sampler, steps, CFG scale and other internals go to a1111.* or comfy.* vendor keys or are dropped. Weighting syntax such as (word:1.3) passes through uninterpreted.

JAI inside raster files. A raster that was rendered from, or described by, a JAI document may carry the canonical JSON form in a PNG iTXt chunk with keyword jai, an empty language tag and an empty translated keyword (compressed or not), or in XMP as the simple property jai:document in the namespace https://jai.gnist.ai/ns/xmp/1.0/ (preferred prefix jai). This lets the editable source travel with the pixels.

Placeholders. BlurHash and ThumbHash serve the same purpose as preview with smaller payloads; see § 8.1.8 preview.

Provenance. IPTC digital source types and C2PA manifests describe how an image was made; JAI describes what it should show. A renderer that writes files sets the former (§ 13.10 Provenance).

Other dialects. --ar 16:9 → aspect=16:9; --no text → avoid=text; --seed N → seed=N; --niji → style=anime; --style raw → dropped.

Appendix D. Serving .jai files

This appendix is informative.

Apache (.htaccess):

AddType image/jai .jai
<FilesMatch "\.jai$">
    Header set Access-Control-Allow-Origin "*"
</FilesMatch>

nginx:

types { image/jai jai; }
location ~ \.jai$ { add_header Access-Control-Allow-Origin "*"; }

GitHub Pages serves .jai as application/octet-stream; decoders inspect the bytes regardless (§ 7.3 Media type and file extension).

Appendix E. Stability and change policy

This appendix is informative.

Acknowledgements

The first edition was shaped by an adversarial review of the design across grammar, authoring, web platform, security, rendering, file format, agent authoring, accessibility and prior-art lenses, whose findings are recorded in the repository’s design record.

Index

Terms defined by this specification

References

Normative References

[CSP3]
Mike West; Antonio Sartori. Content Security Policy Level 3. URL: https://w3c.github.io/webappsec-csp/
[ENCODING]
Anne van Kesteren. Encoding Standard. Living Standard. URL: https://encoding.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC1951]
P. Deutsch. DEFLATE Compressed Data Format Specification version 1.3. May 1996. Informational. URL: https://www.rfc-editor.org/info/rfc1951/
[RFC1952]
P. Deutsch. GZIP file format specification version 4.3. May 1996. Informational. URL: https://www.rfc-editor.org/info/rfc1952/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[RFC2397]
L. Masinter. The "data" URL scheme. August 1998. Proposed Standard. URL: https://www.rfc-editor.org/info/rfc2397/
[RFC5234]
D. Crocker, Ed.; P. Overell. Augmented BNF for Syntax Specifications: ABNF. January 2008. Internet Standard. URL: https://www.rfc-editor.org/info/rfc5234/
[RFC8174]
B. Leiba. Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. May 2017. Best Current Practice. URL: https://www.rfc-editor.org/info/rfc8174/
[RFC8259]
T. Bray, Ed.. The JavaScript Object Notation (JSON) Data Interchange Format. December 2017. Internet Standard. URL: https://www.rfc-editor.org/info/rfc8259/
[RFC8785]
A. Rundgren; B. Jordan; S. Erdtman. JSON Canonicalization Scheme (JCS). June 2020. Informational. URL: https://www.rfc-editor.org/info/rfc8785/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WCAG22]
Michael Cooper; et al. Web Content Accessibility Guidelines (WCAG) 2.2. URL: https://w3c.github.io/wcag/guidelines/22/

Non-Normative References

[AGENT-SKILLS]
Agent Skills specification. URL: https://agentskills.io/specification
[BLURHASH]
Dag Ågren. BlurHash. URL: https://blurha.sh/
[C2PA]
C2PA Technical Specification. URL: https://c2pa.org/specifications/specifications/2.1/specs/C2PA_Specification.html
[IPTC-PHOTOMETADATA]
IPTC Photo Metadata Standard. URL: https://iptc.org/standards/photo-metadata/iptc-standard/
[RFC6838]
N. Freed; J. Klensin; T. Hansen. Media Type Specifications and Registration Procedures. January 2013. Best Current Practice. URL: https://www.rfc-editor.org/info/rfc6838/
[THUMBHASH]
Evan Wallace. ThumbHash: A very compact representation of an image placeholder. URL: https://evanw.github.io/thumbhash/