iComic format draft 0.3.1 — The Gremmoire Reader

iComic format — provisional draft 0.3.1

This specification is provisional. It lets the Reader and the authoring workflow be tested together, but it is not yet a stable interchange or archival standard. Fields, limits, profiles, and migration rules may change before a stable release.

Think of iComic as a page-first interactive-comic format, not a video file wearing a comic interface. Each page owns a set of layered panels and moves through discrete reveal steps.

Animation, audio, backgrounds, captions, haptics, and optional depth can respond to those steps. The essential page must still remain readable without any of them.

Working identity

{
  "format": "valentine.interactive-comic",
  "schemaVersion": "0.3.1"
}

The main development container is .icomic. Underneath the custom extension, it is an ordinary ZIP archive. Ordinary .zip files also remain accepted during development.

Draft 0.3.1 adds two things: a bounded media catalog and ordered local alternatives. The current Reader can still read drafts 0.3 and 0.2, but those drafts do not gain guarantees for fields they never declared.

Media-profile principle

The formats named here are first-class options, not universal winners. Results depend on the source, content, encoder, chosen quality, target browser and operating system, and the author’s experience and preference.

In practice, flat line art may need a different choice from painted pages, and voice may need a different choice from ambience or short motion panels.

Draft 0.3.1 standardizes the combinations worth optimizing deeply while keeping fallbacks explicit and ordered. The format and Reader may add compatibility layers where practical, but packages cannot assume that every browser includes every decoder. The Reader will not silently download a heavyweight decoder just to play an optional variant.

Current first-class delivery targets are:

Role Preferred high-efficiency choices Practical compatibility choices
Static image AVIF or WebP WebP, JPEG, or PNG where appropriate
Lossy audio Opus or AAC-LC in M4A/MP4 MP3 or browser-supported Ogg audio
Lossless audio FLAC PCM WAV when size is deliberately accepted
Video AV1 in WebM/MP4 H.264/AVC in MP4

WavPack and H.266/VVC can be useful authoring/master inputs, especially when their quality-to-size trade-offs suit the source. They are not 0.3.1 browser-delivery promises. An authoring tool may accept them, then produce the ordered web-delivery variants declared in the package.

Animated AVIF and GIF remain outside the current static-image contract.

Minimum manifest

{
  "format": "valentine.interactive-comic",
  "schemaVersion": "0.3.1",
  "id": "my-comic",
  "title": "My Comic",
  "pages": [
    {
      "id": "page-1",
      "panels": [
        {
          "id": "page-1-art",
          "type": "image",
          "src": "assets/pages/page-001.webp",
          "alt": "Describe the page art here."
        }
      ]
    }
  ]
}

The assets catalog is optional. A single-source manifest remains valid, and its src fields resolve exactly as before.

my-comic.icomic                    # ZIP file with a custom extension
|-- comic.json                    # REQUIRED at archive root
|-- assets/
|   |-- panels/
|   |   |-- bush.avif
|   |   `-- bush.webp
|   |-- backgrounds/
|   |   |-- dusk.avif
|   |   `-- dusk.webp
|   |-- audio/
|   |   |-- rain.opus
|   |   |-- rain.m4a
|   |   `-- branch-snap.flac
|   `-- video/
|       |-- branch-rattle-av1.webm
|       `-- branch-rattle-h264.mp4
`-- README.txt                    # Optional production notes; ignored

The current archive rules are straightforward:

Packaging performance

The Reader opens comic.json first. It then resolves media for the page being viewed, and it may warm a bounded part of the next page while idle.

For ZIP compression, use this working split:

Keep authoring masters outside the .icomic. The future packer should choose this profile deterministically and record every decision in its report.

Media delivery and asset catalog

Top-level assets

assets is an optional array of logical media assets. One manifest may declare at most 10,000 assets. Each asset may declare at most four ordered variants.

{
  "assets": [
    {
      "id": "page-1-art",
      "kind": "image",
      "fallback": "assets/pages/page-001.webp",
      "delivery": {
        "profile": "web-balanced",
        "selection": "first-supported"
      },
      "variants": [
        {
          "src": "assets/pages/page-001.avif",
          "type": "image/avif"
        },
        {
          "src": "assets/pages/page-001.webp",
          "type": "image/webp"
        }
      ]
    }
  ]
}

Each asset uses these fields:

Field Type Meaning
id string Unique logical asset ID.
kind image, audio, or video Required role shared by every variant.
fallback relative path Required baseline source and one of the declared variant paths.
delivery object Optional profile label plus selection behavior for this logical asset.
variants array One to four ordered local alternatives.

Each variant uses these fields:

Field Type Meaning
src relative path Required local sibling/archive path.
type MIME string Required declared media type, consistent with kind and the accepted file type.
codecs string Optional codec token or codec list used by browser capability checks, especially for audio and video.

There are three important path rules:

A declared type is a claim the package must satisfy. It cannot be used to disguise an extension.

delivery.profile is a short authoring label recorded for inspection. It does not grant features or choose a decoder.

The only 0.3.1 selection mode is first-supported. Variants are tried in manifest order, subject to the browser’s declared support and successful decoding. If delivery data is omitted, it normalizes to an empty profile and first-supported.

Referencing a logical asset

An image background, image/video panel, or audio cue may add an asset field:

{
  "type": "image",
  "asset": "page-1-art",
  "src": "assets/pages/page-001.webp"
}

The reference must exist, and its kind must match how it is used:

For draft compatibility, src remains required on media-bearing content. It must exactly equal the referenced asset’s fallback.

This gives older Readers a usable baseline file, while 0.3.1 Readers may select a better supported variant. The fallback is not a fifth hidden variant.

For page media and pixel budgets, all alternatives count as one logical asset. The largest alternative is used for that logical asset’s accounting instead of summing equivalent encodings. Every stored byte still counts toward archive and entry limits.

Fully annotated example

JSON cannot contain comments, so notes follow the example.

{
  "format": "valentine.interactive-comic",
  "schemaVersion": "0.3.1",
  "id": "bush-at-dusk-test",
  "title": "Bush at Dusk — Interaction Test",
  "creator": "ValentineDesigns",
  "description": "A non-canon technical sample for reveals and media selection.",
  "language": "en",
  "direction": "ltr",

  "assets": [
    {
      "id": "dusk-background",
      "kind": "image",
      "fallback": "assets/backgrounds/dusk.webp",
      "delivery": {
        "profile": "web-balanced",
        "selection": "first-supported"
      },
      "variants": [
        { "src": "assets/backgrounds/dusk.avif", "type": "image/avif" },
        { "src": "assets/backgrounds/dusk.webp", "type": "image/webp" }
      ]
    },
    {
      "id": "bush-art",
      "kind": "image",
      "fallback": "assets/panels/bush.webp",
      "delivery": {
        "profile": "web-balanced",
        "selection": "first-supported"
      },
      "variants": [
        { "src": "assets/panels/bush.avif", "type": "image/avif" },
        { "src": "assets/panels/bush.webp", "type": "image/webp" }
      ]
    },
    {
      "id": "rain-bed",
      "kind": "audio",
      "fallback": "assets/audio/rain.m4a",
      "delivery": {
        "profile": "web-balanced",
        "selection": "first-supported"
      },
      "variants": [
        {
          "src": "assets/audio/rain.opus",
          "type": "audio/ogg",
          "codecs": "opus"
        },
        {
          "src": "assets/audio/rain.m4a",
          "type": "audio/mp4",
          "codecs": "mp4a.40.2"
        }
      ]
    },
    {
      "id": "branch-snap",
      "kind": "audio",
      "fallback": "assets/audio/branch-snap.flac",
      "delivery": {
        "profile": "lossless-effects",
        "selection": "first-supported"
      },
      "variants": [
        {
          "src": "assets/audio/branch-snap.flac",
          "type": "audio/flac"
        }
      ]
    },
    {
      "id": "branch-motion",
      "kind": "video",
      "fallback": "assets/video/branch-rattle-h264.mp4",
      "delivery": {
        "profile": "web-balanced",
        "selection": "first-supported"
      },
      "variants": [
        {
          "src": "assets/video/branch-rattle-av1.webm",
          "type": "video/webm",
          "codecs": "av01.0.08M.08"
        },
        {
          "src": "assets/video/branch-rattle-h264.mp4",
          "type": "video/mp4",
          "codecs": "avc1.4D401F"
        }
      ]
    }
  ],

  "experimental": {
    "customContainer": true,
    "animatedPanels": true,
    "audioCues": true,
    "dynamicBackgrounds": true,
    "depth": true,
    "timedReveals": true
  },

  "presentation": {
    "fit": "contain",
    "startMode": "panel",
    "pageGap": 18
  },

  "pages": [
    {
      "id": "dusk-page",
      "label": "Dusk / three reveals",
      "aspectRatio": "2/3",

      "background": {
        "type": "image",
        "asset": "dusk-background",
        "src": "assets/backgrounds/dusk.webp",
        "fit": "cover",
        "depth": -2
      },

      "cues": [
        {
          "id": "rain-bed-cue",
          "type": "audio",
          "trigger": "enter",
          "asset": "rain-bed",
          "src": "assets/audio/rain.m4a",
          "volume": 0.3,
          "loop": true,
          "channel": "ambience"
        }
      ],

      "panels": [
        {
          "id": "bush",
          "type": "image",
          "asset": "bush-art",
          "src": "assets/panels/bush.webp",
          "alt": "A dense bush moving against the still dusk.",
          "frame": {
            "x": 5,
            "y": 8,
            "width": 90,
            "height": 46,
            "rotation": 0,
            "radius": 1.5
          },
          "fit": "cover",
          "depth": 1,
          "z": 2,
          "reveal": {
            "atStep": 0,
            "transition": "fade",
            "durationMs": 500
          },
          "animation": {
            "type": "drift",
            "durationMs": 8000,
            "amount": 0.35
          }
        },
        {
          "id": "branches",
          "type": "video",
          "asset": "branch-motion",
          "src": "assets/video/branch-rattle-h264.mp4",
          "alt": "Branches rattle and settle.",
          "frame": { "x": 5, "y": 8, "width": 90, "height": 46 },
          "fit": "cover",
          "depth": 2,
          "z": 3,
          "reveal": {
            "atStep": 1,
            "transition": "focus",
            "durationMs": 240
          },
          "playback": {
            "autoplay": true,
            "muted": true,
            "loop": false,
            "playsInline": true
          },
          "cues": [
            {
              "id": "branch-snap-cue",
              "type": "audio",
              "trigger": "reveal",
              "asset": "branch-snap",
              "src": "assets/audio/branch-snap.flac",
              "delayMs": 80,
              "volume": 0.65,
              "channel": "effects"
            },
            {
              "id": "branch-caption",
              "type": "caption",
              "trigger": "reveal",
              "text": "Branches rattle.",
              "durationMs": 1800
            }
          ]
        },
        {
          "id": "figure",
          "type": "image",
          "src": "assets/panels/figure.webp",
          "alt": "A tired figure rises behind the bush.",
          "frame": { "x": 12, "y": 33, "width": 76, "height": 58 },
          "fit": "contain",
          "depth": 3,
          "z": 4,
          "reveal": {
            "atStep": 2,
            "transition": "rise",
            "durationMs": 650
          }
        }
      ]
    }
  ]
}

The first image or media alternative is the preferred encoding. The baseline fallback stays authoritative for older Readers.

The FLAC effect uses one variant because lossless quality is intentional in that case. A package does not need to manufacture alternatives that add no practical value.

Top-level fields

Field Type Current meaning / normalization
format string Expected: valentine.interactive-comic; legacy valentine.interactive-story remains readable during the draft.
schemaVersion string Current draft: 0.3.1; 0.3 and 0.2 remain accepted legacy drafts.
id string Stable work identifier; unsafe characters normalize to -.
title string Defaults to Untitled comic, maximum 240 characters.
creator string Optional, maximum 240 characters.
description string Optional, maximum 1,000 characters.
language string BCP-47-like language hint; defaults to en, maximum 35 characters.
direction ltr or rtl Defaults to ltr; full RTL UX validation remains open.
context array Optional Reader-wide author context and linked terms, maximum 100 entries.
assets array Optional bounded catalog of logical media and ordered variants.
experimental object Declares provisional capabilities expected by the work.
presentation object Default fit, mode, and page gap.
pages array Required, 1–10,000 pages.

Each context entry uses a stable id, short label, title, and concise body. An optional exact term links matching words in text panels to a hover/focus preview and click/tap appendix entry; optional details supplies the expanded explanation. The same text-only shape is used by prose Reader documents. Entries without term remain available from Comic margins.

experimental

The current boolean declarations are:

These fields describe expectations. They are not security permissions. A Reader may disable an enhancement, but it must still expose the readable core.

presentation

Field Allowed values Default
fit contain, width, height contain
startMode panel, page panel
pageGap number, clamped 0–100 18

Page, panel, background, and cue fields

Page

Field Type Current meaning
id string Unique across the comic.
label string Timeline label, maximum 120 characters.
aspectRatio number or ratio string Examples: 0.6667, 2/3, 2:3; defaults to 2:3.
background object Page-level color, gradient, image, or provisional dynamic background.
panels array Layered page content.
cues array Page-scoped events such as ambience on entry.

Background

{
  "type": "image",
  "asset": "dusk-background",
  "src": "assets/backgrounds/dusk.webp",
  "fit": "cover",
  "depth": -1
}

Recognized background types are color, gradient, image, and dynamic. The optional asset field is valid only for an image background.

dynamic remains a placeholder category. Its value accepts only a narrow, sanitized color/gradient grammar. Network-capable CSS functions and URL/data/blob schemes fall back safely.

Panel

Field Type Current meaning
id string Unique within its page.
type enum image, text, video, gif, pdf-page, or reserved canvas.
asset string Optional logical asset reference for an image or video panel.
src string Relative baseline media path; must equal the asset fallback when asset is present.
alt string Nonvisual description, maximum 500 characters.
text string Plain text-panel content, maximum 10,000 characters.
frame object Percentage geometry, rotation, and corner radius.
fit enum contain, cover, or fill; defaults to cover.
depth number Optional pointer/tilt depth, clamped -10 to 10.
z integer Layer order, clamped -100 to 1,000.
reveal object Discrete step, transition, and duration.
animation object Optional decorative animation.
playback object Video playback hints.
cues array Events associated with this panel/reveal.

Frames use page-relative percentages. Reveal steps are nonnegative integers.

Recognized transitions are:

Decorative animation types are:

Decorative animation must not carry information that disappears under reduced motion.

Playback hints are autoplay, muted, loop, and playsInline. Browser autoplay and codec rules override these hints.

Cue

{
  "id": "soft-signal",
  "type": "audio",
  "trigger": "reveal",
  "asset": "signal-audio",
  "src": "assets/audio/signal.m4a",
  "text": "Optional visible caption",
  "delayMs": 0,
  "volume": 0.65,
  "loop": false,
  "channel": "effects"
}

Recognized cue types are:

Only an audio cue may reference an audio asset. The common current triggers are enter and reveal.

Channel replacement, crossfade, persistence, loop lifetime, and mastering metadata remain provisional.

The current Reader dispatches cues for every panel revealed on the same step. It cancels pending delayed cues when another reveal begins or the page changes.

Current safety and resource limits

The current rejection ceilings are:

These are safety limits, not performance promises. A valid package may still be too demanding for a particular phone, browser, or operating system.

Reader conformance principles

A future conforming Reader should:

  1. Show essential visual and text content without sound, haptics, animation, depth, sensors, or a particular optional codec.
  2. Preserve author-defined reveal order while allowing a complete page reveal.
  3. Respect reduced motion and mute settings immediately.
  4. Provide keyboard and button navigation independent of gestures.
  5. Never execute arbitrary scripts, HTML, CSS, plugins, or network calls from an iComic.
  6. Select only bounded local variants and warn about unsupported required features rather than silently losing comic-critical content.
  7. Expose authored alt text, captions, and reading order.
  8. Tear down media, timers, sensors, decoders, and temporary URLs when leaving a work.

Packaging checklist

See Authoring and encoding for the planned content-aware encoding, validation, packaging, and upload workflow.

Source availability and bespoke Readers

The iComic specification and observable behavior may be discussed publicly. The Gremmoire Reader and its authoring/tooling source remain closed.

The implementation is tuned around ValentineDesigns’ work and publishing workflow. Publishing it as a general-purpose codebase would imply portability and support that it is not designed to provide.

For a bespoke Reader or authoring workflow adapted to another site, contact [email protected].

A recipient who receives a bespoke source copy is free to host, adapt, modify, and otherwise use that supplied source as they wish. This permission applies to the delivered bespoke copy. It does not make the Gremmoire Reader or the general toolchain open source.

Open format questions