draft optional
This NIP extends NIP-94 (File Metadata) to support multiple resolution variants of an image, enabling bandwidth-efficient responsive image delivery while preserving content-addressed integrity via Blossom.
Modern devices have vastly different display capabilities:
Currently, Nostr clients either:
Additionally, images from cameras contain EXIF metadata that may leak:
This NIP enables clients to:
A responsive image set is represented by a kind 1063 event (per NIP-94) containing multiple imeta tags, one per variant. This follows the pattern established by NIP-71 for video variants.
Each imeta tag MUST include:
url - Blossom URL for this variantx - SHA-256 hash of this variant's file contentm - MIME type (same as original)dim - Dimensions as <width>x<height>variant - Size category identifierThe variant field identifies the resolution category:
| Variant | Target Width | Use Case |
|---|---|---|
thumb | 128px | Previews, galleries, feed thumbnails |
mobile-sm | 512px | Small mobile portrait |
mobile-lg | 1024px | Large mobile, small tablets |
desktop-sm | 1536px | Laptops |
desktop-md | 2048px | Standard desktops |
desktop-lg | 2560px | Large/HiDPI displays |
original | native | Full resolution, EXIF stripped |
thumb variant SHOULD include a blurhash for placeholder displayRecommended JPEG quality settings per variant:
thumb: 70mobile-sm: 75mobile-lg: 80desktop-sm: 85desktop-md: 88desktop-lg: 90original: 92For PNG images, use maximum compression without quality loss.
Clients SHOULD use next-larger selection: pick the smallest variant >= target width.
targetWidth = containerWidth * devicePixelRatio
selectedVariant = smallest variant where variant.width >= targetWidth
This ensures the client only needs to downscale slightly (or not at all), rather than upscaling which would cause blur. If no variant is large enough, use the largest available.
All variants generated:
{
"kind": 1063,
"pubkey": "<publisher-pubkey>",
"created_at": 1234567890,
"content": "Sunset over the mountains",
"tags": [
["imeta",
"url https://blossom.example.com/abc123def456.jpg",
"x abc123def456789...",
"m image/jpeg",
"dim 4032x3024",
"variant original"
],
["imeta",
"url https://blossom.example.com/def456abc789.jpg",
"x def456abc789012...",
"m image/jpeg",
"dim 2560x1920",
"variant desktop-lg"
],
["imeta",
"url https://blossom.example.com/789abc123def.jpg",
"x 789abc123def345...",
"m image/jpeg",
"dim 2048x1536",
"variant desktop-md"
],
["imeta",
"url https://blossom.example.com/012def456abc.jpg",
"x 012def456abc678...",
"m image/jpeg",
"dim 1536x1152",
"variant desktop-sm"
],
["imeta",
"url https://blossom.example.com/234abc567def.jpg",
"x 234abc567def890...",
"m image/jpeg",
"dim 1024x768",
"variant mobile-lg"
],
["imeta",
"url https://blossom.example.com/345abc789def.jpg",
"x 345abc789def901...",
"m image/jpeg",
"dim 512x384",
"variant mobile-sm"
],
["imeta",
"url https://blossom.example.com/678def012abc.jpg",
"x 678def012abc234...",
"m image/jpeg",
"dim 128x96",
"variant thumb",
"blurhash eVF$^OI:${M{o#*0-nNFxakD"
],
["x", "abc123def456789..."],
["x", "def456abc789012..."],
["x", "789abc123def345..."],
["x", "012def456abc678..."],
["x", "234abc567def890..."],
["x", "345abc789def901..."],
["x", "678def012abc234..."]
],
"id": "<event-id>",
"sig": "<signature>"
}
Note: The separate x tags duplicate the hashes from the imeta tags. This redundancy enables standard NIP-01 tag queries (#x) to discover the binding event by any variant hash, while the imeta tags provide the full metadata for each variant.
Only smaller variants generated (no desktop variants):
{
"kind": 1063,
"pubkey": "<publisher-pubkey>",
"created_at": 1234567890,
"content": "Quick snapshot",
"tags": [
["imeta",
"url https://blossom.example.com/small123.jpg",
"x small123456789...",
"m image/jpeg",
"dim 1200x900",
"variant original"
],
["imeta",
"url https://blossom.example.com/small456.jpg",
"x small456789012...",
"m image/jpeg",
"dim 1024x768",
"variant mobile-lg"
],
["imeta",
"url https://blossom.example.com/small789.jpg",
"x small789012345...",
"m image/jpeg",
"dim 512x384",
"variant mobile-sm"
],
["imeta",
"url https://blossom.example.com/small012.jpg",
"x small012345678...",
"m image/jpeg",
"dim 128x96",
"variant thumb",
"blurhash eVF$^OI:${M{o#*0"
]
],
"id": "<event-id>",
"sig": "<signature>"
}
imeta tagse tag or URL)x hashimeta tags to extract available variants- Current viewport width - Device pixel ratio - Network conditions (optional)
blurhash placeholder while loadingx tag (optional but recommended)function selectVariant(variants, viewportWidth, pixelRatio = 1):
targetWidth = viewportWidth * pixelRatio
# Sort variants by width ascending
sorted = variants.sortBy(v => v.width)
# Find smallest variant >= target width
for variant in sorted:
if variant.width >= targetWidth:
return variant
# If none large enough, return largest available
return sorted.last()
Relays SHOULD index kind 1063 events by all x hashes present in imeta tags. This enables clients to discover the binding event when they only have one variant's hash.
Query example:
["REQ", "sub1", {"kinds": [1063], "#x": ["<any-variant-hash>"]}]
Critical: Blossom blob hashes alone are meaningless without the binding event. A client finding a hash on a Blossom server cannot determine:
The binding event (kind 1063) is the authoritative source. Discovery flow:
imeta tag)Clients SHOULD verify that downloaded content matches the x hash in the imeta tag. This prevents:
Unlike NIP-96's ?w= parameter, this NIP requires all transforms to happen client-side before upload. This preserves the content-addressing guarantee: the hash always matches the file content.
Publishing clients MUST strip EXIF and other metadata to protect user privacy. This includes:
Kind 1063 events are immutable. To update an image (e.g., add a missing variant), publish a new event and update references. Consider using addressable events (kind 31063 with d tag) if updates are needed frequently.
variant will still store and serve the eventsoriginal variant ensures full-resolution access is always availableimeta tag as the primary file