Read and write AI-generated image metadata
npm install @enslo/sd-metadata


๐ฏ๐ต ๆฅๆฌ่ช็ใฏใใกใ
A TypeScript library to read and write metadata embedded in AI-generated images.
- Multi-format Support: PNG (tEXt / iTXt), JPEG (COM / Exif), WebP (Exif)
- Unified API: Simple read() and write() functions work across all formats
- TypeScript Native: Written in TypeScript with full type definitions included
- Zero Dependencies: Works in Node.js and browsers without any external dependencies
- Format Conversion: Seamlessly convert metadata between PNG, JPEG, and WebP
- Metadata Preservation: Preserves original metadata structure when converting formats (e.g., PNG โ JPEG โ PNG maintains all original data)
``bash`
npm install @enslo/sd-metadata
| Tool | PNG | JPEG | WebP |
| ------ | :---: | :----: | :----: |
| NovelAI * | โ
| ๐๏ธ | โ
|
| ComfyUI * | โ
| ๐๏ธ | ๐๏ธ |
| AUTOMATIC1111 | โ ๏ธ | โ ๏ธ | โ ๏ธ |
| Forge / Forge Neo | โ
| โ
| โ
|
| InvokeAI | โ
| ๐๏ธ | ๐๏ธ |
| SwarmUI * | โ
| โ
| โ
|
| Civitai | โ ๏ธ | โ
| โ ๏ธ |
| TensorArt | โ
| ๐๏ธ | ๐๏ธ |
| Stability Matrix | โ
| ๐๏ธ | ๐๏ธ |
| HuggingFace Space | โ
| ๐๏ธ | ๐๏ธ |
| Ruined Fooocus | โ
| ๐๏ธ | ๐๏ธ |
| Easy Diffusion | โ ๏ธ | โ ๏ธ | โ ๏ธ |
| Fooocus | โ ๏ธ | โ ๏ธ | โ ๏ธ |
Legend:
- โ
Fully Supported - Formats natively supported by the tool, verified with sample files
- ๐๏ธ Extended Support - Formats not natively supported by the tool, but sd-metadata enables read/write through custom format conversion. Supports round-trip conversion back to native formats.
- โ ๏ธ Experimental - Implemented by analyzing reference code or documentation, not verified with actual sample files. May not extract all metadata fields correctly.
Extended Support Examples:
- Stability Matrix (native: PNG only) โ sd-metadata enables JPEG/WebP support
- NovelAI (native: PNG, WebP) โ sd-metadata enables JPEG support
When you convert from a native format to an extended format and back (e.g., PNG โ JPEG โ PNG), all metadata is preserved.
> [!NOTE]
> \* Tools with format-specific behaviors. See Format-Specific Behaviors for details.
> [!TIP]
> Help us expand tool support! We're actively collecting sample images from experimental tools (Easy Diffusion, Fooocus) and unsupported tools. If you have sample images generated by these or other AI tools, please consider contributing them! See CONTRIBUTING.md for details.
Some tools have specific behaviors when converting between formats:
- ComfyUI JPEG/WebP: Reading supports multiple custom node formats (e.g., save-image-extended), but writing always uses the comfyui-saveimage-plus format for best information preservation and compatibility with ComfyUI's native drag-and-drop workflow loading.Make
- NovelAI WebP: Automatically corrects corrupted UTF-8 in the Description field. WebP โ PNG โ WebP round-trip produces valid, readable metadata but with minor text corrections.
- SwarmUI PNGโJPEG/WebP: Native SwarmUI JPEG/WebP files do not include node information. When converting from PNG, this library preserves the ComfyUI workflow in the field for complete metadata retention (extended support).
ESM (TypeScript / Modern JavaScript):
`typescript`
import { read } from '@enslo/sd-metadata';
CommonJS (Node.js):
`javascript`
const { read } = require('@enslo/sd-metadata');
> [!NOTE]
> All examples below use ESM syntax. CommonJS users can replace import with require.
`typescript
import { read, write } from '@enslo/sd-metadata';
import { readFileSync, writeFileSync } from 'fs';
const imageData = readFileSync('image.png');
const result = read(imageData);
if (result.status === 'success') {
console.log('Tool:', result.metadata.software); // 'novelai', 'comfyui', etc.
console.log('Prompt:', result.metadata.prompt);
console.log('Model:', result.metadata.model?.name);
console.log('Size:', result.metadata.width, 'x', result.metadata.height);
}
`
`typescript
import { read } from '@enslo/sd-metadata';
// Handle file input
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file) return;
const arrayBuffer = await file.arrayBuffer();
const result = read(arrayBuffer);
if (result.status === 'success') {
document.getElementById('tool').textContent = result.metadata.software;
document.getElementById('prompt').textContent = result.metadata.prompt;
document.getElementById('model').textContent = result.metadata.model?.name || 'N/A';
}
});
`
For bookmarklets or userscripts (Tampermonkey, Violentmonkey, etc.), load from jsDelivr CDN:
`javascript
// Import from CDN
import { read } from 'https://cdn.jsdelivr.net/npm/@enslo/sd-metadata@latest/dist/index.js';
const response = await fetch(imageUrl);
const arrayBuffer = await response.arrayBuffer();
const result = read(arrayBuffer);
if (result.status === 'success') {
console.log('Tool:', result.metadata.software);
console.log('Prompt:', result.metadata.prompt);
}
`
> [!TIP]
> For production use, pin to a specific version instead of @latest:`
>
> text`
> https://cdn.jsdelivr.net/npm/@enslo/sd-metadata@1.7.1/dist/index.js
>
Format Conversion
Convert metadata between different image formats:
`typescript
import { read, write } from '@enslo/sd-metadata';
// Read metadata from PNG
const pngData = readFileSync('comfyui-output.png');
const parseResult = read(pngData);
if (parseResult.status === 'success') {
// Convert PNG to JPEG (using your preferred image processing library)
const jpegImageData = convertToJpeg(pngData); // Pseudo-code: use sharp, canvas, etc.
// Embed the metadata into the JPEG
const result = write(jpegImageData, parseResult);
if (result.ok) {
writeFileSync('output.jpg', result.value);
console.log('Image converted to JPEG with metadata preserved');
}
}
`
> [!TIP]
> This library handles metadata read/write only. For actual image format conversion (decoding/encoding pixels), use image processing libraries like sharp, jimp, or browser Canvas API.
Handling Different Result Types
`typescript
import { read } from '@enslo/sd-metadata';
const result = read(imageData);
switch (result.status) {
case 'success':
// Metadata successfully parsed
console.log(Generated by ${result.metadata.software});Prompt: ${result.metadata.prompt}
console.log();
break;
case 'unrecognized':
// Metadata exists but format is not recognized
console.log('Unknown metadata format');
// You can still access raw metadata for debugging:
console.log('Raw chunks:', result.raw);
break;
case 'empty':
// No metadata found
console.log('No metadata in this image');
break;
case 'invalid':
// Corrupted or invalid image data
console.log('Error:', result.message);
break;
}
`
Handling Unrecognized Metadata
When working with metadata from unsupported tools:
`typescript
import { read, write } from '@enslo/sd-metadata';
const source = read(unknownImage);
// source.status === 'unrecognized'
// Write to target image
// - Same format (e.g., PNG โ PNG): metadata preserved as-is
// - Cross-format (e.g., PNG โ JPEG): metadata dropped with warning
const result = write(targetImage, source);
if (result.ok) {
saveFile('output.png', result.value);
if (result.warning) {
// Metadata was dropped during cross-format conversion
console.warn('Metadata was dropped:', result.warning.reason);
}
}
`
Removing Metadata
To strip all metadata from an image:
`typescript
import { write } from '@enslo/sd-metadata';
const result = write(imageData, { status: 'empty' });
if (result.ok) {
writeFileSync('clean-image.png', result.value);
}
`
Writing Metadata in WebUI Format
Create and embed custom metadata in SD WebUI (A1111) format:
`typescript
import { writeAsWebUI } from '@enslo/sd-metadata';
// Create custom metadata from scratch
const metadata = {
software: 'sd-webui',
prompt: 'masterpiece, best quality, 1girl',
negativePrompt: 'lowres, bad quality',
width: 512,
height: 768,
sampling: {
steps: 20,
sampler: 'Euler a',
cfg: 7,
seed: 12345,
},
model: { name: 'model.safetensors' },
};
// Write to any image format (PNG, JPEG, WebP)
const result = writeAsWebUI(imageData, metadata);
if (result.ok) {
writeFileSync('output.png', result.value);
}
`
> [!TIP]
> writeAsWebUI is particularly useful when:
>
> - Creating images programmatically and want to embed generation parameters
> - Converting metadata from proprietary formats to WebUI-compatible format
> - Building tools that need to output WebUI-readable metadata
Formatting Metadata for Display
Convert metadata from any supported tool to a unified, human-readable WebUI format. This normalizes the differences between tools (NovelAI, ComfyUI, Forge, etc.) into a consistent text format:
`typescript
import { read, formatAsWebUI } from '@enslo/sd-metadata';
const result = read(imageData);
if (result.status === 'success') {
// Works with any tool: NovelAI, ComfyUI, Forge, InvokeAI, etc.
const text = formatAsWebUI(result.metadata);
console.log(text);
// Always outputs in consistent WebUI format:
// masterpiece, best quality, 1girl
// Negative prompt: lowres, bad quality
// Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 12345, Size: 512x768, Model: model.safetensors
}
`
> [!NOTE]
> Regardless of which tool generated the image, formatAsWebUI extracts the common generation parameters and formats them in a standardized way. This is ideal for displaying metadata to users without worrying about tool-specific formats.
Reads and parses metadata from an image file.
Returns:
- { status: 'success', metadata, raw } - Successfully parsedmetadata
- : Unified metadata object (see GenerationMetadata)raw
- : Original format-specific data (chunks/segments){ status: 'unrecognized', raw }
- - Image has metadata but not from a known AI toolraw
- : Original metadata preserved for conversion{ status: 'empty' }
- - No metadata found in the image{ status: 'invalid', message? }
- - Corrupted or unsupported image formatmessage
- : Optional error description
Writes metadata to an image file.
Parameters:
- input - Target image file data (PNG, JPEG, or WebP)metadata
- - ParseResult from read()status: 'success'
- or 'empty' - Can write directlystatus: 'unrecognized'
- - Same format: writes as-is; Cross-format: drops metadata with warning
Returns:
- { ok: true, value: Uint8Array, warning?: WriteWarning } - Successfully writtenwarning
- is set when metadata was intentionally dropped (e.g., unrecognized cross-format){ ok: false, error: { type, message? } }
- - Failed. type is one of:'unsupportedFormat'
- : Target image is not PNG, JPEG, or WebP'conversionFailed'
- : Metadata conversion failed (e.g., incompatible format)'writeFailed'
- : Failed to embed metadata into the image
Writes metadata to an image in SD WebUI (A1111) format.
Parameters:
- input - Target image file data (PNG, JPEG, or WebP)metadata
- - Generation metadata to embed
- Can be from any tool or custom-created
- Automatically converted to WebUI format
Returns:
- { ok: true, value: Uint8Array } - Successfully written (returns new image data){ ok: false, error: { type, message? } }
- - Failed. type is one of:'unsupportedFormat'
- : Target image is not PNG, JPEG, or WebP'writeFailed'
- : Failed to embed metadata into the image
Use cases:
- Creating custom metadata for programmatically generated images
- Converting metadata from other tools to WebUI-compatible format
- Building applications that output WebUI-readable metadata
Formats metadata as human-readable text in SD WebUI (A1111) format.
Parameters:
- metadata - Generation metadata from any tool
Returns:
- Human-readable string in WebUI format (plain text)
Output format:
`text`
positive prompt
[character prompts for NovelAI]
Negative prompt: negative prompt
Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 12345, Size: 512x768, ...
Use cases:
- Displaying metadata to users in a consistent format
- Copying generation parameters as text
- Logging or debugging generation settings
Formats raw metadata as plain text.
Parameters:
- raw - Raw metadata from ParseResult (result.raw)
Returns:
- Plain text content from the metadata (multiple entries separated by blank lines)
Use cases:
- Displaying unrecognized metadata to users
- Quick inspection of raw metadata content
- Fallback display when parsing fails
Example:
`typescript
import { read, formatAsWebUI, formatRaw } from '@enslo/sd-metadata';
const result = read(imageData);
switch (result.status) {
case 'success':
console.log(formatAsWebUI(result.metadata));
break;
case 'unrecognized':
console.log(formatRaw(result.raw));
break;
}
`
This section provides an overview of the main types. For complete type definitions, see Type Documentation.
The result of the read() function. It uses a discriminated union with a status field.
`typescript`
type ParseResult =
| { status: 'success'; metadata: GenerationMetadata; raw: RawMetadata }
| { status: 'unrecognized'; raw: RawMetadata }
| { status: 'empty' }
| { status: 'invalid'; message?: string };
Unified metadata structure returned by the read() function. This is a discriminated union of 3 specific metadata types, distinguished by the software field.
Common Fields (Available in All Types):
All metadata types include these base fields:
- prompt: string - Positive prompt textnegativePrompt: string
- - Negative prompt textwidth: number
- - Image width in pixelsheight: number
- - Image height in pixelsmodel?: ModelSettings
- - Model information (name, hash, VAE)sampling?: SamplingSettings
- - Sampling parameters (seed, steps, CFG, sampler, scheduler, clipSkip)hires?: HiresSettings
- - Hires.fix settings (if applied)upscale?: UpscaleSettings
- - Upscale settings (if applied)
Metadata Type Variants:
- NovelAIMetadata (software: 'novelai') characterPrompts?: CharacterPrompt[]
Includes NovelAI-specific fields for V4 character placement:
- - Per-character prompts with positionsuseCoords?: boolean
- - Use character coordinates for placementuseOrder?: boolean
- - Use character order
- ComfyUIMetadata (software: 'comfyui' | 'tensorart' | 'stability-matrix' | 'swarmui') nodes: ComfyNodeGraph
Includes ComfyUI workflow graph:
- (required for comfyui/tensorart/stability-matrix)nodes?: ComfyNodeGraph
- (optional for swarmui - only in PNG format)
- StandardMetadata (software: 'sd-webui' | 'forge' | 'invokeai' | 'civitai' | ...)
Baseline metadata without tool-specific extensions. Used by most SD WebUI-based tools.
Type Definition:
`typescript`
type GenerationMetadata =
| NovelAIMetadata
| ComfyUIMetadata
| StandardMetadata;
Usage Example:
`typescript
const result = read(imageData);
if (result.status === 'success') {
const metadata = result.metadata;
// Access common fields
console.log('Prompt:', metadata.prompt);
console.log('Model:', metadata.model?.name);
console.log('Seed:', metadata.sampling?.seed);
// Type-specific handling using discriminated union
if (metadata.software === 'novelai') {
// TypeScript knows this is NovelAIMetadata
console.log('Character prompts:', metadata.characterPrompts);
} else if (
metadata.software === 'comfyui' ||
metadata.software === 'tensorart' ||
metadata.software === 'stability-matrix'
) {
// TypeScript knows this is BasicComfyUIMetadata (nodes always present)
console.log('Node count:', Object.keys(metadata.nodes).length);
} else if (metadata.software === 'swarmui') {
// TypeScript knows this is SwarmUIMetadata (nodes optional)
if (metadata.nodes) {
console.log('Workflow included');
}
}
}
`
See Type Documentation for detailed interface definitions of each metadata type.
Preserves the original metadata structure for round-trip conversions.
`typescript`
type RawMetadata =
| { format: 'png'; chunks: PngTextChunk[] }
| { format: 'jpeg'; segments: MetadataSegment[] }
| { format: 'webp'; segments: MetadataSegment[] };
> [!TIP]
> For TypeScript users: All types are exported and available for import.
>
> `typescript`
> import type {
> ParseResult,
> GenerationMetadata,
> ModelSettings,
> SamplingSettings
> } from '@enslo/sd-metadata';
>
>
> Use your IDE's IntelliSense for auto-completion and inline documentation.
For detailed documentation of all exported types including ModelSettings, SamplingSettings, and format-specific types, see the Type Documentation.
`bashInstall dependencies
npm install
MIT