Skip to content

Animated Media (Reaction Clips) #2403

Description

@Letdown2491

Animated Media (Reaction Clips)

draft optional

Defines a content-addressed event for short animated media ("GIFs") and a tagging convention for discovering it, so GIF-style browse and search run on the nostr network layer with no centralized catalog or paid API. Rendering animated media needs no protocol; this NIP is about discovery. Using a clip in a note requires nothing from this NIP (see Using clips in notes).

Storage, trust, moderation and curation are delegated to existing NIPs (94/95, 85, 32/56, 30). A prior attempt to put discovery on the file server (nips#1337) was rejected for overloading it; this keeps storage dumb and does discovery with events, NIP-50 and trusted pubkeys.

The clip event

A clip is a regular event (kind TBD, in the 10009999 range; this draft uses 1090). It is immutable and permanent.

The media is identified by the x sha256 of its bytes, carried in imeta (NIP-92). Identical bytes from different publishers share one hash; clients dedup by hash and aggregate signal across copies (see Spam and trust).

{
  "kind": 1090,
  "content": "Optional caption (indexed by NIP-50).",
  "tags": [
    ["imeta",
      "url https://blossom.example/<sha256>.webp",
      "m image/webp",
      "x <sha256-hex of the media>",
      "dim 480x270",
      "dur 2.4",
      "image https://blossom.example/<sha256>-poster.webp",
      "alt person covering their face in disbelief"
    ],
    ["x", "<sha256-hex of the media>"],
    ["L", "emotion"],
    ["l", "disbelief", "emotion"],
    ["l", "facepalm", "emotion"],
    ["t", "this is fine"]
  ]
}
  • REQUIRED: an imeta with url (or embedded ref, see Storage), m,x, dim; a top-level x tag mirroring the media hash (so relays can index it); at least one emotion l.
  • RECOMMENDED: image (poster frame), alt.
  • OPTIONAL: dur, blurhash, size, more l facets (capped at 6), t descriptors, content.

Mimetype SHOULD be image/webp, video/mp4 or image/gif.

Why regular, not addressable? An earlier draft used an addressable event keyed by the media hash so metadata stayed editable. But this spec's authoritative labeling is emergent from engagement aggregated across all events for a hash, not any single author's editable record, so replaceable semantics bought nothing the model uses. Corrections are a new event plus a NIP-09 delete. The one property addressability provided for free is recovered at read time; see Spam and trust.

Storage

Identity is always the x sha256, so either model works and clients need not care which:

  • Referenced (default): bytes behind a Blossom / NIP-94 URL in imeta url. Use for anything above sticker size.
  • Embedded: bytes stored in-relay per NIP-95. Use only for tiny stickers.

Resilience against dead links

Because the media hash x is the identity, a rotted imeta url is recoverable and SHOULD NOT be treated as clip loss. Clients SHOULD:

  • Publish to redundancy. Upload to every server in the publisher's Blossom server list (BUD-03, kind:10063), mirroring where supported (BUD-04), so a single server outage does not kill the clip.
  • Heal on read. When imeta url fails, take the x hash and refetch the blob from the publisher's kind:10063 servers, then from well-known servers. The blob is byte-identical by definition, so recovery is safe and automatic.
  • Optionally carry hints. A clip or note MAY reference the blob as a BUD-10 blossom: URI (?xs= server hints, ?as= author pubkeys, ?sz= size) so any client can resolve it without the origin server.

Servers that return NIP-94 tags on upload (BUD-08) let clients populate imeta directly from the upload response.

Tagging

Two tiers, both on top of the free NIP-50 text index over content/alt:

  • Emotions (l with L:emotion): a controlled, relay-indexed, flat set, at least one and at most six, all equal. Labels come from a published emotion namespace (an addressable event clients fetch); one reference namespace is the default, forks are allowed, adoption decides the canonical set. There is no author-declared "primary"; the dominant bucket is emergent from engagement. Browse is a #l filter.
  • Long tail (t + text): freeform memes/characters/shows. Powerful but stuffable, so clients SHOULD weight t by publisher trust. Search is NIP-50.

Discovery

  • Browse: {"kinds":[1090], "#l":["facepalm"]}, relay-indexed.
  • Search: NIP-50 over content/alt/t.
  • Rank by zaps/engagement and NIP-85 publisher rank, not recency. The emotion a clip ranks highest in is its dominant bucket.

A catalog-scale corpus is millions of events; this NIP moves the indexing cost off generic file servers and onto opt-in catalog relays that choose to index this kind. Size them deliberately.

Using clips in notes

Nothing in this NIP is required to post a GIF. A clip is inserted into a kind-1 note (or any content event) as its media URL, exactly like any other media on nostr:

  • The note content carries the imeta url (e.g. https://blossom.example/<sha256>.webp).
  • The note SHOULD carry a matching imeta tag (NIP-92) so rendering clients get the mime type, dimensions, and alt text.

Because the note transports a URL, not this custom kind, it renders in every client that previews media URLs or honors NIP-92 so no adoption of this NIP required. Clients that do not implement NIP-92 still show the raw URL, which most render inline by file extension. Prefer image/gif or image/webp for the note URL when maximum cross-client rendering matters; use video/mp4 where the target clients are known to render video.

A client posting a clip SHOULD include the media x hash in the note's imeta (it is already present there per NIP-92) and MAY add a q/e reference to the clip event, so figgy-aware readers can link back to the catalog entry and so usage feeds the ranking signal (Spam and trust, item 2). This is strictly additive since the note renders identically in clients that ignore it.

Spam and trust

Publishing cannot be gated; filtering is at read/index time, layered:

  1. Publisher rank (NIP-85 30382): the load-bearing signal. Hide or down-rank low-rank pubkeys, but never hard-gate; unranked newcomers SHOULD be shown deprioritized so they are not filtered identically to spam.
  2. Per-clip quality: read zaps/engagement directly, aggregated by media x across all events (and notes) that carry it. (A provider MAY publish NIP-85 30384 per-clip assertions as a precomputed convenience, but this is optional and off the critical path.)
  3. Hash dedup: aggregate across copies so reposting cannot flood. When aggregating, clients MUST collapse events sharing the same (pubkey, x) to the newest one, so a single publisher cannot stuff emotion buckets by reposting the same bytes with different labels. This recovers the anti-stuffing property that an addressable event would have enforced at the relay.
  4. Cap: at most six emotion facets, and no self-declared primary, so a publisher cannot stuff buckets or self-promote into top results.

Moderation, curation

NIP-32 content-warning labels and NIP-56 reports. Curated "clip packs" (the NIP-30 set pattern, referencing clips by e tag + media x) are deferred to a companion NIP.

Open questions

  • Final kind number (draft placeholder 1090 chosen as unassigned and free of live traffic; the earlier 1064 was dropped for colliding with nostrsite theme events); emotion cap value.
  • Reference emotion namespace: seed label set and steward pubkey.
  • Whether dur/loop are standardized imeta keys or extensions.
  • The note-side clip reference is settled on imeta x (always present) as the interoperable minimum; whether to also standardize a q vs e reference to the clip event for richer back-linking is still open.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions