🦕 TypeScript / JavaScript Interface¶
NGFF-Zarr provides a TypeScript implementation for working with OME-Zarr data structures in Deno, Node.js, and browser environments. The TypeScript API mirrors the Python interface, providing a familiar and consistent experience across languages.
✨ Features¶
🦕 Deno-first: Built for Deno with first-class TypeScript support
📦 Universal compatibility: Works in Deno, Node.js, and browsers
🔍 Type-safe: Full TypeScript support with Zod schema validation
🗂️ OME-Zarr support: Read and write OME-Zarr v0.4, v0.5, and v0.6 (v0.6 adds RFC-5 coordinate systems and transformations), plus the opt-in development version
0.9.dev1which adopts RFC-3 (extended axis count, names, types and order)🧪 Well-tested: Comprehensive test suite with browser validation
🏗️ Mirrors Python API: Familiar interfaces for Python users
📖 Lazy loading: Efficient handling of large datasets
🌐 Web ready: No filesystem dependencies, works with remote stores
📦 Installation¶
JSR (Deno and Node.js)¶
The package is published to JSR (JavaScript Registry) for use with Deno and modern Node.js:
Deno:
import * as ngffZarr from "jsr:@fideus-labs/ngff-zarr";
Node.js with JSR support:
npx jsr add @fideus-labs/ngff-zarr
import * as ngffZarr from "@fideus-labs/ngff-zarr";
npm (Node.js and Bundlers)¶
For traditional Node.js projects and bundlers like webpack, Vite, or esbuild:
npm install @fideus-labs/ngff-zarr
import * as ngffZarr from "@fideus-labs/ngff-zarr";
Browser (CDN)¶
For direct browser usage via CDN:
<script type="module">
import * as ngffZarr from "https://esm.sh/@fideus-labs/ngff-zarr";
// Your code here
</script>
🚀 Quick Start¶
Reading OME-Zarr Files¶
Read an OME-Zarr file and access the multiscale data:
import { fromOmeZarr } from "@fideus-labs/ngff-zarr";
// Read from a local file (Deno/Node.js)
const multiscales = await fromOmeZarr("path/to/image.ome.zarr");
// Read from a remote URL (works in all environments)
const remoteMultiscales = await fromOmeZarr(
"https://example.com/data.ome.zarr"
);
// Access the image data
console.log(`Loaded ${multiscales.images.length} scale levels`);
console.log(`Image shape: ${multiscales.images[0].data.shape}`);
console.log(`Data type: ${multiscales.images[0].data.dtype}`);
// Access metadata
console.log(`Axes: ${JSON.stringify(multiscales.metadata.axes)}`);
console.log(`Version: ${multiscales.metadata.version}`);
Validating OME-Zarr Metadata¶
Enable validation to ensure OME-Zarr files conform to the specification:
import { fromOmeZarr } from "@fideus-labs/ngff-zarr";
// Validate during reading
const multiscales = await fromOmeZarr("image.ome.zarr", {
validate: true,
});
// Specify expected version
const multiscalesV6 = await fromOmeZarr("image.ome.zarr", {
validate: true,
version: "0.6",
});
Creating and Writing OME-Zarr Files¶
Create a simple OME-Zarr file from scratch:
import {
createAxis,
createDataset,
createMetadata,
createMultiscales,
createNgffImage,
toOmeZarr,
} from "@fideus-labs/ngff-zarr";
// Create image data (256x256 grayscale)
const data = new Uint8Array(256 * 256);
for (let i = 0; i < data.length; i++) {
data[i] = Math.floor(Math.random() * 256);
}
// Create an NgffImage with metadata
const image = createNgffImage(
data.buffer,
[256, 256], // shape
"uint8", // dtype
["y", "x"], // dimension names
{ y: 1.0, x: 1.0 }, // scale (pixel spacing)
{ y: 0.0, x: 0.0 } // translation (origin)
);
// Create OME-Zarr metadata
const axes = [
createAxis("y", "space", "micrometer"),
createAxis("x", "space", "micrometer"),
];
const datasets = [
createDataset(
"0", // path
[1.0, 1.0], // scale
[0.0, 0.0] // translation
),
];
const metadata = createMetadata(axes, datasets, "my_image");
// Create multiscales container
const multiscales = createMultiscales([image], metadata);
// Write to OME-Zarr
await toOmeZarr("output.ome.zarr", multiscales);
Generating Multiscale Pyramids¶
Create multiscale image pyramids with downsampling:
import {
createNgffImage,
toMultiscales,
toOmeZarr,
Methods,
} from "@fideus-labs/ngff-zarr";
// Create base image
const data = new Uint8Array(512 * 512);
const image = createNgffImage(
data.buffer,
[512, 512],
"uint8",
["y", "x"],
{ y: 1.0, x: 1.0 },
{ y: 0.0, x: 0.0 }
);
// Generate multiscale pyramid with 2x and 4x downsampling
const multiscales = await toMultiscales(image, {
scaleFactors: [2, 4],
method: Methods.ITKWASM_GAUSSIAN,
chunks: 128,
});
// Write the pyramid
await toOmeZarr("pyramid.ome.zarr", multiscales);
Displacement and coordinate fields (v0.6)¶
OME-Zarr v0.6 (RFC-5) stores displacement and coordinate fields as ordinary
multiscale images. The vector-component axis, in the channel position, carries
type: "displacement" (or type: "coordinate") instead of type: "channel".
Like a channel/component axis it is discrete (it indexes vector components
rather than a continuous coordinate). Set the type with the axesTypes option
of the image (createNgffImage / toNgffImage), which maps a dimension name to
its axis type:
import {
createNgffImage,
toMultiscales,
toOmeZarr,
} from "@fideus-labs/ngff-zarr";
// A 2-component displacement field over a yx image: the "c" dimension holds the
// (y, x) displacement vector, one component per output spatial axis.
const data = new Float32Array(2 * 256 * 256);
const field = await createNgffImage(
data.buffer,
[2, 256, 256],
"float32",
["c", "y", "x"],
{ c: 1.0, y: 1.0, x: 1.0 },
{ c: 0.0, y: 0.0, x: 0.0 },
"displacement_field",
{ c: "displacement" },
);
const multiscales = await toMultiscales(field);
await toOmeZarr("displacement.ome.zarr", multiscales, { version: "0.6" });
The axis type round-trips through reading and writing.
OME-Zarr v0.6 also models the displacements and coordinates coordinate
transformations (the Displacements and Coordinates types) that reference such
a field from a registered image. Like the other v0.6 transforms, they round-trip
through reading and writing.
A typed component axis says what the channels are; it does not say between which
coordinate systems the field maps, so nothing can apply it. declareFieldTransform
adds that entry, validated:
import { declareFieldTransform } from "@fideus-labs/ngff-zarr";
// The standalone store -- the artifact a registration emits: the field maps a
// spatial coordinate system onto itself through its own level-0 array.
const declared = declareFieldTransform(multiscales);
await toOmeZarr("displacement.ome.zarr", declared, { version: "0.6" });
For a field written beside the image it displaces, declare the entry on the
image’s multiscales instead, with path naming the field’s array and
inputSystem/outputSystem referencing the image’s own coordinate systems.
See RFC-5: Coordinate Systems and Transformations for the full transformation model, including how to write an image and its transformation (an affine, or a displacement/coordinate field) into a single store.
🌍 Platform Support¶
Deno¶
Full native support with TypeScript:
import * as ngffZarr from "@fideus-labs/ngff-zarr";
const multiscales = await ngffZarr.fromOmeZarr("./data.ome.zarr");
Node.js¶
Compatible with Node.js 18+ (ESM and CommonJS):
// ESM
import { fromOmeZarr } from "@fideus-labs/ngff-zarr";
// CommonJS
const { fromOmeZarr } = require("@fideus-labs/ngff-zarr");
Browser¶
Works in modern browsers with ES modules:
<!DOCTYPE html>
<html>
<head>
<title>OME-Zarr in Browser</title>
</head>
<body>
<script type="module">
import { fromOmeZarr } from "https://esm.sh/@fideus-labs/ngff-zarr";
// Load from remote URL
const multiscales = await fromOmeZarr(
"https://example.com/data.ome.zarr"
);
console.log("Loaded:", multiscales.images.length, "scales");
</script>
</body>
</html>
Browser Limitations:
Cannot read from local filesystem (use HTTP URLs instead)
FileSystemStore is not available in browsers
Use FetchStore for remote data access
📚 API Reference¶
Core Types¶
NgffImage¶
Represents a single-scale image with associated metadata:
interface NgffImage {
data: DaskArray; // Lazy array data
dims: string[]; // Dimension names ["t", "c", "z", "y", "x"]
scale: Record<string, number>; // Pixel spacing per dimension
translation: Record<string, number>; // Origin offset
name?: string; // Optional image name
axesUnits?: Record<string, Units>; // Optional units per axis
}
Example:
import { createNgffImage } from "@fideus-labs/ngff-zarr";
const image = createNgffImage(
arrayBuffer,
[100, 100],
"uint16",
["y", "x"],
{ y: 0.5, x: 0.5 },
{ y: 0.0, x: 0.0 }
);
NgffMultiscales¶
Container for multiple resolution levels:
interface NgffMultiscales {
images: NgffImage[]; // Array of scale levels
metadata: Metadata; // OME-Zarr metadata
scaleFactors?: (number | Record<string, number>)[]; // Scale factors
method?: Methods; // Downsampling method
chunks?: Record<string, number>; // Chunk sizes
}
Metadata¶
OME-Zarr metadata structure:
interface Metadata {
axes: Axis[]; // Dimension definitions
datasets: Dataset[]; // Scale level paths and transforms
coordinateTransformations?: CoordinateTransformation[];
name?: string;
version?: "0.4" | "0.5" | "0.6";
type?: string; // Downsampling method type
metadata?: Record<string, unknown>;
}
Axis¶
Dimension axis definition:
interface Axis {
name: string; // "t", "c", "z", "y", "x"
type?: "time" | "channel" | "space";
unit?: Units; // UDUNITS-2 identifier
}
I/O Functions¶
fromOmeZarr()¶
Read an OME-Zarr file:
async function fromOmeZarr(
store: string | MemoryStore | FetchStore,
options?: {
validate?: boolean;
version?: "0.4" | "0.5" | "0.6" | "0.9.dev1";
}
): Promise<NgffMultiscales>
Parameters:
store: Path to OME-Zarr (string), URL (string), or Zarr store objectoptions.validate: Enable metadata validation (default: false)options.version: Expected OME-Zarr version
Returns: NgffMultiscales object with all scale levels
Example:
// Basic reading
const ms = await fromOmeZarr("data.ome.zarr");
// With validation
const validatedMs = await fromOmeZarr("data.ome.zarr", {
validate: true,
version: "0.6",
});
// From URL
const remoteMs = await fromOmeZarr("https://example.com/data.ome.zarr");
toOmeZarr()¶
Write an NgffMultiscales object to OME-Zarr:
async function toOmeZarr(
store: string,
multiscales: NgffMultiscales,
options?: {
version?: "0.4" | "0.5" | "0.6" | "0.9.dev1";
chunksPerShard?: number | number[] | Record<string, number>;
consolidateMetadata?: boolean;
}
): Promise<void>
Parameters:
store: Output path for OME-Zarrmultiscales: NgffMultiscales object to writeoptions.version: OME-Zarr version (default: “0.5”). For a.ozxpath it must be a version stored in Zarr v3 – “0.5”, “0.6” or “0.9.dev1”options.chunksPerShard: Store each block of chunks as one Zarr v3 shard – how many chunks a shard spans: one count for every axis, one per axis in order, or per axis name (an axis left out spans 1). v0.5 and later. Omitted, a directory store is not sharded, while a.ozxpath is sharded 2 chunks a shard along every axisoptions.consolidateMetadata: Inline every array’s metadata into the rootzarr.json(default:true)
Example:
// Basic writing
await toOmeZarr("output.ome.zarr", multiscales);
// With version specification
await toOmeZarr("output.ome.zarr", multiscales, { version: "0.6" });
// With sharding (v0.5)
await toOmeZarr("output.ome.zarr", multiscales, {
version: "0.5",
chunksPerShard: { z: 2, y: 2, x: 2 },
});
Sharding. A shard spans chunksPerShard chunks along each axis, held to
the fewest whole chunks that cover the axis – the same grid the Python writer
lays out for the same request. The chunks keep their codecs inside the
sharding_indexed codec, whose index is checksummed with crc32c as
zarr-python and zarrs write it. Shards are encoded on the codec workers, a
whole shard per write. fromOmeZarr reads sharded stores back through the
same workers, fetching a shard’s index and then only the chunks a selection
touches; an in-memory Map store works too.
Consolidated metadata. By default the writer finishes by inlining every
array’s zarr.json into the root group’s own zarr.json. A reader that
understands the block resolves the whole hierarchy from that one document
instead of one request per scale level, which matters most over HTTP and for
RFC-9 archives. zarr-python is such a reader, and so is the Python package,
which consolidates every store it writes – writing the block keeps the two
implementations’ output equivalent. fromOmeZarr is one as well: it
resolves every scale level’s array from the root document.
Some backends run their own consolidation and reject the block – Icechunk is the motivating case – so it can be turned off:
await toOmeZarr("output.ome.zarr", multiscales, {
consolidateMetadata: false,
});
The flag governs the root document alone; the arrays are written identically
either way. Re-writing a consolidated store with consolidateMetadata: false
leaves it unconsolidated rather than stale, because a Zarr v3 write replaces
the root document wholesale. toOmeZarrOzx / toOmeZarrOzxData accept the
same option for RFC-9 archives. This mirrors the Python package’s
to_ome_zarr(..., consolidate_metadata=False).
OME-Zarr Zip (.ozx) archives. A .ozx output path writes an
RFC-9 zipped OME-Zarr:
the store is built in memory and packed into a single ZIP with the root
zarr.json as its first entry and the OME-Zarr version in the ZIP comment.
RFC-9 is defined on Zarr v3, so any version stored in Zarr v3 can be zipped –
“0.5” (the default), “0.6” or “0.9.dev1” – while “0.4” (Zarr v2) throws. The
archive carries the same root document a directory store at that version
would, so a v0.6 archive keeps its RFC-5 coordinate systems and transforms.
chunksPerShard shards an archive’s arrays as it does a directory store’s;
its entries are stored uncompressed, so a reader range-reads single chunks out
of a shard. As in the Python writer, an archive is sharded by default: a shard
spans 2 chunks along every axis unless chunksPerShard says otherwise.
// v0.5 archive (default)
await toOmeZarr("output.ozx", multiscales);
// v0.6 archive
await toOmeZarr("output.ozx", multiscales, { version: "0.6" });
// The ZIP bytes without touching the filesystem (browsers, downloads)
import { toOmeZarrOzxData } from "@fideus-labs/ngff-zarr";
const zipData = await toOmeZarrOzxData(multiscales, { version: "0.6" });
Read an archive back with fromOmeZarr and zarrita’s ZipFileStore:
import { ZipFileStore } from "@zarrita/storage";
const store = ZipFileStore.fromBlob(new Blob([zipData]));
const multiscales = await fromOmeZarr(store, { validate: true });
toMultiscales()¶
Generate multiscale pyramid from a single image:
async function toMultiscales(
image: NgffImage,
options?: {
scaleFactors?: (number | Record<string, number>)[];
method?: Methods;
chunks?: number | number[] | Record<string, number>;
}
): Promise<NgffMultiscales>
Parameters:
image: Base NgffImage to downsampleoptions.scaleFactors: Downsampling factors (default: [2, 4])options.method: Downsampling method (default: ITKWASM_GAUSSIAN)options.chunks: Chunk sizes for output
Returns: NgffMultiscales with generated pyramid levels
Axes are normalized to the OME-Zarr order, time then channel then space, so a
non-canonical input such as (z, y, c, x) yields (c, z, y, x) and the data is
reordered with it. An axis model outside the (t, c, z, y, x) vocabulary is
left alone: it carries no spec ordering to normalize to. A positional chunks
array indexes the dims you passed and follows them through the reordering.
Example:
// Basic pyramid generation
const ms = await toMultiscales(image);
// Custom scale factors
const ms2 = await toMultiscales(image, {
scaleFactors: [2, 4, 8],
method: Methods.ITKWASM_GAUSSIAN,
});
// With custom chunks
const ms3 = await toMultiscales(image, {
scaleFactors: [{ x: 2, y: 2 }, { x: 4, y: 4 }],
chunks: 256,
});
Factory Functions¶
createNgffImage()¶
Create an NgffImage from raw data:
function createNgffImage(
buffer: ArrayBuffer,
shape: number[],
dtype: string,
dims: string[],
scale: Record<string, number>,
translation: Record<string, number>,
name?: string
): NgffImage
createAxis()¶
Create an axis definition:
function createAxis(
name: string,
type: "time" | "channel" | "space",
unit?: Units
): Axis
createDataset()¶
Create a dataset entry:
function createDataset(
path: string,
scale: number[],
translation: number[]
): Dataset
createMetadata()¶
Create OME-Zarr metadata:
function createMetadata(
axes: Axis[],
datasets: Dataset[],
name?: string
): Metadata
createMultiscales()¶
Create an NgffMultiscales container:
function createMultiscales(
images: NgffImage[],
metadata: Metadata,
scaleFactors?: (number | Record<string, number>)[],
method?: Methods
): NgffMultiscales
Validation¶
Schema Validation with Zod¶
The package includes Zod schemas for runtime validation:
import {
MetadataSchema,
NgffImageSchema,
MultiscalesSchema,
validateMetadata,
} from "@fideus-labs/ngff-zarr";
// Validate metadata
const metadata = { axes: [...], datasets: [...] };
const validated = MetadataSchema.parse(metadata);
// Safe parsing (returns result object)
const result = MetadataSchema.safeParse(metadata);
if (result.success) {
console.log("Valid metadata:", result.data);
} else {
console.error("Validation errors:", result.error.issues);
}
// Utility function
const validMetadata = validateMetadata(metadata);
Downsampling Methods¶
Available downsampling methods via the Methods enum:
enum Methods {
ITKWASM_GAUSSIAN = "itkwasm_gaussian", // Gaussian smoothing (default)
ITKWASM_BIN_SHRINK = "itkwasm_bin_shrink", // Bin shrinking
ITKWASM_LABEL_IMAGE = "itkwasm_label_image", // Label-aware downsampling
}
Example:
import { toMultiscales, Methods } from "@fideus-labs/ngff-zarr";
const pyramid = await toMultiscales(image, {
method: Methods.ITKWASM_GAUSSIAN,
scaleFactors: [2, 4],
});
💡 Examples¶
Example 1: Convert Image Format¶
Read an image and convert to OME-Zarr:
import {
itkImageToNgffImage,
toMultiscales,
toOmeZarr,
} from "@fideus-labs/ngff-zarr";
// Load image using itk-wasm
const itkImage = await loadImageFromFile("input.png");
// Convert to NgffImage
const ngffImage = await itkImageToNgffImage(itkImage);
// Generate pyramid
const multiscales = await toMultiscales(ngffImage);
// Write to OME-Zarr
await toOmeZarr("output.ome.zarr", multiscales);
Example 2: Process Remote Data¶
Work with remote OME-Zarr files:
import { fromOmeZarr } from "@fideus-labs/ngff-zarr";
// Read from S3 or other HTTP-accessible storage
const multiscales = await fromOmeZarr(
"https://s3.amazonaws.com/bucket/data.ome.zarr"
);
// Access the highest resolution
const fullRes = multiscales.images[0];
console.log(`Shape: ${fullRes.data.shape}`);
console.log(`Spacing: ${JSON.stringify(fullRes.scale)}`);
// Access a lower resolution
const lowRes = multiscales.images[multiscales.images.length - 1];
console.log(`Low-res shape: ${lowRes.data.shape}`);
Example 3: Metadata Inspection¶
Examine OME-Zarr metadata:
import { fromOmeZarr } from "@fideus-labs/ngff-zarr";
const multiscales = await fromOmeZarr("data.ome.zarr");
// Inspect axes
multiscales.metadata.axes.forEach(axis => {
console.log(`Axis ${axis.name}: type=${axis.type}, unit=${axis.unit}`);
});
// Inspect datasets
multiscales.metadata.datasets.forEach((dataset, i) => {
console.log(`Scale ${i}: path=${dataset.path}`);
dataset.coordinateTransformations.forEach(transform => {
if (transform.type === "scale") {
console.log(` Scale: ${transform.scale}`);
}
});
});
// Check version
console.log(`OME-Zarr version: ${multiscales.metadata.version}`);
Example 4: Custom Chunk Sizes¶
Control chunking for optimal performance:
import {
createNgffImage,
toMultiscales,
toOmeZarr,
} from "@fideus-labs/ngff-zarr";
const image = createNgffImage(
buffer,
[1024, 1024, 1024], // Large 3D volume
"uint16",
["z", "y", "x"],
{ z: 1.0, y: 0.5, x: 0.5 },
{ z: 0.0, y: 0.0, x: 0.0 }
);
// Use smaller chunks for better streaming
const multiscales = await toMultiscales(image, {
chunks: { z: 64, y: 128, x: 128 },
scaleFactors: [2, 4, 8],
});
await toOmeZarr("large_volume.ome.zarr", multiscales);
Example 5: TypeScript Type Safety¶
Leverage TypeScript’s type system:
import type {
NgffImage,
NgffMultiscales,
Metadata,
Axis,
} from "@fideus-labs/ngff-zarr";
import { fromOmeZarr, validateMetadata } from "@fideus-labs/ngff-zarr";
// Type-safe function
async function processImage(path: string): Promise<NgffMultiscales> {
const multiscales: NgffMultiscales = await fromOmeZarr(path);
// TypeScript knows the structure
const axes: Axis[] = multiscales.metadata.axes;
const firstImage: NgffImage = multiscales.images[0];
return multiscales;
}
// Type-safe metadata validation
function ensureValidMetadata(metadata: unknown): Metadata {
return validateMetadata(metadata);
}
Example 6: JavaScript Usage¶
Pure JavaScript usage without TypeScript:
// JavaScript (Node.js or browser)
import { fromOmeZarr, toOmeZarr, createNgffImage } from "@fideus-labs/ngff-zarr";
// Read OME-Zarr
const multiscales = await fromOmeZarr("data.ome.zarr");
// Create new image
const data = new Uint8Array(100 * 100);
const image = createNgffImage(
data.buffer,
[100, 100],
"uint8",
["y", "x"],
{ y: 1.0, x: 1.0 },
{ y: 0.0, x: 0.0 }
);
// Write to OME-Zarr
await toOmeZarr("output.ome.zarr", image);
🔧 Advanced Usage¶
Working with Different Store Types¶
import * as zarr from "zarrita";
import { fromOmeZarr } from "@fideus-labs/ngff-zarr";
// Memory store
const memoryStore = new Map<string, Uint8Array>();
const ms1 = await fromOmeZarr(memoryStore);
// Fetch store (HTTP/HTTPS)
const fetchStore = new zarr.FetchStore("https://example.com/data.ome.zarr");
const ms2 = await fromOmeZarr(fetchStore);
// File system store (Node.js/Deno only)
import { FileSystemStore } from "@zarrita/storage";
const fsStore = new FileSystemStore("/path/to/data.ome.zarr");
const ms3 = await fromOmeZarr(fsStore);
Converting Between OME-Zarr Versions¶
import { fromOmeZarr, toOmeZarr } from "@fideus-labs/ngff-zarr";
// Convert from v0.4 to v0.5
const multiscales = await fromOmeZarr("data_v04.ome.zarr");
await toOmeZarr("data_v05.ome.zarr", multiscales, { version: "0.5" });
// Convert from v0.5 to v0.4
const multiscalesV5 = await fromOmeZarr("data_v05.ome.zarr");
await toOmeZarr("data_v04.ome.zarr", multiscalesV5, { version: "0.4" });
Upgrading OME-Zarr Store Versions¶
The conversion above re-reads and re-writes the entire pyramid. To change only
the recorded specification version of an existing store, upgradeOmeZarr()
mirrors the Python upgrade_ome_zarr and offers the same two modes:
function upgradeOmeZarr(
input: string | MemoryStore | FetchStore | Readable,
options?: {
output?: string | MemoryStore; // FetchStore is read-only, not a destination
version?: "0.4" | "0.5" | "0.6" | "0.9.dev1"; // default "0.6"
validate?: boolean;
overwrite?: boolean; // write-to-new-store only; default true
},
): Promise<void>;
In-place, metadata-only. When output is omitted (or resolves to the same
store as input) and the source and target share the same underlying Zarr
format – 0.5 to 0.6, both Zarr v3 – only the root group’s zarr.json is
rewritten and every array chunk in the store is left byte-for-byte untouched,
avoiding the data loss of a naive “read, erase, re-write” upgrade. An in-place
upgrade that would cross the Zarr v2/v3 boundary
(OME-Zarr 0.4 to 0.5/0.6) throws with guidance to pass output instead.
import {
createAxis,
createDataset,
createMetadata,
createMultiscales,
createNgffImage,
toOmeZarr,
upgradeOmeZarr,
type MemoryStore,
} from "@fideus-labs/ngff-zarr";
// Build a small image and write it to a MemoryStore at version 0.5 (Zarr v3).
const data = new Uint8Array(4 * 32 * 32);
const image = await createNgffImage(
data.buffer,
[4, 32, 32],
"uint8",
["z", "y", "x"],
{ z: 1.0, y: 1.0, x: 1.0 },
{ z: 0.0, y: 0.0, x: 0.0 },
);
const axes = [
createAxis("z", "space", "micrometer"),
createAxis("y", "space", "micrometer"),
createAxis("x", "space", "micrometer"),
];
const datasets = [createDataset("0", [1.0, 1.0, 1.0], [0.0, 0.0, 0.0])];
const multiscales = createMultiscales([image], createMetadata(axes, datasets));
const store: MemoryStore = new Map();
await toOmeZarr(store, multiscales, { version: "0.5" });
// Upgrade 0.5 -> 0.6 in place: only the root group's metadata is rewritten,
// leaving every array chunk in the store untouched.
await upgradeOmeZarr(store, { version: "0.6" });
Write-to-new-store. When output is a store distinct from input, the
source is read lazily and re-written to output at the requested version
through the standard write pipeline. Every transition among 0.4, 0.5, 0.6 and
0.9.dev1 works in this mode whenever the target version can express the axis
model, and the source store is never mutated. An RFC-3 axis model is refused
below 0.9.dev1, so a store using one only converts upward.
import { upgradeOmeZarr, type MemoryStore } from "@fideus-labs/ngff-zarr";
// `source` is an existing 0.4 store (e.g. a MemoryStore or FetchStore).
const target: MemoryStore = new Map();
await upgradeOmeZarr(source, { output: target, version: "0.6" });
In-place upgrade of a local path store is Node/Deno-only – the browser build
ships no filesystem store – while MemoryStore inputs work in every
environment, consistent with the other I/O functions. In the browser, use a
MemoryStore for an in-place upgrade, or pass output.
High Content Screening (HCS)¶
Work with plate and well data:
import { fromHcsZarr } from "@fideus-labs/ngff-zarr";
// Read HCS plate
const plate = await fromHcsZarr("plate.ome.zarr");
console.log(`Plate: ${plate.metadata.name}`);
console.log(`Wells: ${plate.metadata.wells.length}`);
// Access specific well
const well = plate.getWell("A", "1");
if (well) {
console.log(`Well A/1 has ${well.images.length} field(s)`);
}
🆚 Comparison with Python¶
The TypeScript API closely mirrors the Python interface:
Python |
TypeScript |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JSON Schema validation |
JSON Schema validation |
Key Differences¶
Naming conventions: Python uses snake_case, TypeScript uses camelCase
Data handling: Python uses Dask arrays, TypeScript uses array metadata
Async: TypeScript I/O operations are async by default
🐛 Troubleshooting¶
Browser CORS Issues¶
When accessing remote OME-Zarr files in the browser, ensure CORS headers are set:
// This may fail due to CORS
const ms = await fromOmeZarr("https://example.com/data.ome.zarr");
// Solution: Ensure server sends proper CORS headers
// Access-Control-Allow-Origin: *
File System Access in Browser¶
Browsers cannot access local filesystem:
// ❌ Won't work in browser
const ms = await fromOmeZarr("/path/to/local/file.ome.zarr");
// ✅ Use HTTP URLs instead
const ms = await fromOmeZarr("http://localhost:8000/file.ome.zarr");
Memory Management¶
For large datasets, zarrita handles lazy loading automatically:
// Data is loaded lazily - metadata read immediately
const multiscales = await fromOmeZarr("large_file.ome.zarr");
// Actual array data loaded on access
const shape = multiscales.images[0].data.shape; // Fast
// Actual data retrieval would happen when reading chunks
📚 External Resources¶
🤝 Contributing¶
Contributions are welcome! See the development documentation for details on:
Setting up the development environment
Running tests
Building the npm package
Browser testing with Playwright