v3.0.0

<bmx-deep-zoom>

A viewer for very large pictures - microscope slides, scans, maps, panels of artwork, satellite and drone mosaics - opened in the page with nothing uploaded, at any size, smoothly: only the tiles the view needs are read, at the detail the screen needs.

17 properties · 8 events · 16 methods · 5 parts

Example

Try it with your own files in confidence: they are opened by your browser, on your device. Nothing is uploaded, no copy reaches us or anyone else, and nothing is kept once you close this tab.

A small copy of the whole set

The sample is the Mandelbrot set at 1,048,576 × 786,432 pixels - more than 800 gigapixels - drawn tile by tile on background threads as you zoom, so no file is downloaded. A few places are marked; choose one on the Annotations tab and Go to it, or press N for the next. Draw rectangles, ellipses, polygons, freehand outlines, points and measurements; turn the view with R; compare it with a second colouring side by side or with the swipe divider. Open your own TIFF (also BigTIFF and slide scans such as Aperio SVS and OME-TIFF), JPEG or PNG, or give the address of a Deep Zoom, IIIF or Zoomify picture.

Show markup
<bmx-deep-zoom id="ex-dz" style="--bmx-deep-zoom-height: 38rem">
  <span class="ex-dz-tag" data-x="223226" data-y="393216">A small copy of the whole set</span>
</bmx-deep-zoom>
<p class="note">The sample is the Mandelbrot set at 1,048,576 × 786,432 pixels - more than 800 gigapixels - drawn tile by tile on background threads as you zoom, so no file is downloaded. A few places are marked; choose one on the Annotations tab and Go to it, or press N for the next. Draw rectangles, ellipses, polygons, freehand outlines, points and measurements; turn the view with R; compare it with a second colouring side by side or with the swipe divider. Open your own TIFF (also BigTIFF and slide scans such as Aperio SVS and OME-TIFF), JPEG or PNG, or give the address of a Deep Zoom, IIIF or Zoomify picture.</p>

<div class="row" style="gap: 0.75rem; flex-wrap: wrap; align-items: center; margin-block: 0.5rem 1.5rem">
  <label>Open your own picture <input type="file" id="ex-dz-file" accept=".tif,.tiff,.svs,.btf,.jpg,.jpeg,.png,.webp" /></label>
  <button type="button" id="ex-dz-sample">Show the sample again</button>
  <button type="button" id="ex-dz-compare">Compare two colourings</button>
  <form id="ex-dz-url-form" style="display: inline-flex; gap: 0.5rem; align-items: center">
    <label>Picture address <input type="url" id="ex-dz-url" placeholder="https://…/info.json or …/photo.dzi" size="32" /></label>
    <button type="submit">Open</button>
  </form>
</div>
<p class="note" id="ex-dz-out" role="status"></p>

<style>
  .ex-dz-tag {
    padding: 0.125rem 0.5rem;
    border-radius: 999px;
    background: #111827;
    color: #fff;
    font: 600 0.75rem system-ui, sans-serif;
    white-space: nowrap;
  }
</style>

<script type="module">
  await customElements.whenDefined('bmx-deep-zoom');
  const viewer = document.getElementById('ex-dz');
  const out = document.getElementById('ex-dz-out');
  viewer.addEventListener('bmxLoad', e => {
    const { name, width, height, levels, compare } = e.detail;
    out.textContent = `${compare ? 'Compared with ' : ''}${name}: ${width.toLocaleString()} × ${height.toLocaleString()} pixels, ${levels} levels.`;
  });
  viewer.addEventListener('bmxSelect', e => (out.textContent = e.detail ? `Chose ${e.detail.label || e.detail.shape}.` : ''));
  viewer.addEventListener('bmxMeasure', e => (out.textContent = `Measured ${e.detail.text}.`));
  viewer.addEventListener('bmxAnnotationChange', e => (out.textContent = `${e.detail.annotations.length} annotations (${e.detail.change}).`));
  viewer.addEventListener('bmxSave', e => (out.textContent = `Saved ${e.detail.name} (${Math.round(e.detail.data.size / 1024)} KB).`));
  viewer.addEventListener('bmxError', e => (out.textContent = e.detail.message));
  document.getElementById('ex-dz-file').addEventListener('change', e => {
    if (e.target.files.length) viewer.open(e.target.files[0]);
  });
  document.getElementById('ex-dz-sample').addEventListener('click', () => viewer.loadSample());
  document.getElementById('ex-dz-compare').addEventListener('click', async () => {
    await viewer.openCompare('sample:ember');
    viewer.compare = 'side';
  });
  document.getElementById('ex-dz-url-form').addEventListener('submit', e => {
    e.preventDefault();
    const url = document.getElementById('ex-dz-url').value.trim();
    if (url) viewer.open(url);
  });
  await viewer.loadSample();
</script>

WHAT IT DOES

  • Opens pyramids on servers (Deep Zoom, IIIF Image API 2 and 3, Zoomify) and local or remote TIFF and BigTIFF files of any size, tiled or in strips, with their own smaller levels (slide scans such as Aperio SVS, OME-TIFF, VIPS pyramids) or without; ordinary JPEG, PNG and WebP too. Pyramids a picture lacks are built in the background.
  • Moves and zooms with the pointer, the wheel, pinches and the keyboard; turns in steps of 90 degrees or freely, and mirrors; an overview map with the view on it to click or drag.
  • Compares two pictures, side by side or with a swipe divider, moving together (pictures of different sizes by their proportions) or apart.
  • Annotations: rectangles, ellipses, polygons, freehand outlines, points and measurements, labelled, coloured, moved and reshaped; fixed regions from the page; saved and opened as JSON or GeoJSON (QuPath's form).
  • Calibrated measuring: the pixel size from the file (Aperio, OME-XML, resolution tags) or by drawing a line of known length; lengths and areas in the unit that reads best; a scale bar.
  • Overlays: the page's own elements, placed in the element with data-x and data-y (and data-width, data-height), stay on their spot of the picture as it moves.
  • Saves the view, or a region at full detail, as PNG.

Properties

PropertyAttributeTypeDefaultDescription
annotations annotations BmxZoomAnnotation[] | string [] Annotations (a list, or its JSON, or GeoJSON).
compactBelow compact-below number 760 Below this width (pixels) the panel goes under the picture.
compare compare BmxDeepZoomCompare 'off' How two pictures are compared: one picture only, side by side, or a swipe divider.
compareSrc compare-src string — A second picture, to compare with the first.
flip flip boolean false Mirrored left to right.
label label string — The accessible name of the viewer.
linked linked boolean true Two pictures move together.
maxZoom max-zoom number 8 The closest zoom, in screen pixels for each picture pixel.
minimap minimap boolean true Show the overview map.
noFiles no-files boolean false No opening of files (pictures come from src or open).
pixelSize pixel-size number — The size of a pixel in micrometres; taken from the picture when it says, and set by calibrating.
readOnly read-only boolean false Look only: no drawing, changing or calibrating.
rotation rotation number 0 The turn, in degrees clockwise.
showAnnotations show-annotations boolean true Show the annotations.
src src string — The picture to open: a TIFF, JPEG or PNG address, a Deep Zoom, IIIF or Zoomify description's, or sample.
strings strings Partial<BmxDeepZoomStrings> | string — Replacements for any of the wording (an object, or its JSON).
tool tool BmxDeepZoomTool 'pan' The active tool.

Events

EventDetailDescription
bmxAnnotationChange { annotations: BmxZoomAnnotation[]; change: "change" | "import" | "remove" | "add"; id?: string | undefined; } Fired when annotations are added, changed or removed.
bmxCalibrate { pixelSize: number | null; } Fired when the pixel size is set by calibrating (or cleared).
bmxError { message: string; name?: string | undefined; } Fired when a picture or file cannot be opened.
bmxLoad BmxDeepZoomInfo & { compare: boolean; } Fired when a picture has opened.
bmxMeasure { pixels: number; micrometres: number | null; text: string; annotation: BmxZoomAnnotation; } Fired when a measurement is drawn.
bmxSave BmxDeepZoomSaveDetail Fired before a file is downloaded. Cancelable: cancel to keep the file yourself.
bmxSelect BmxZoomAnnotation | null Fired when an annotation is chosen, or the choice cleared (null).
bmxViewChange BmxDeepZoomViewState Fired as the view moves, zooms or turns (at most once a frame).

Methods

MethodSignatureDescription
closeCompare closeCompare() => Promise<void> Close the second picture.
download download(kind?: "view" | "region" | "selection" | "annotations" | "geojson") => Promise<void> Download the view, the whole picture (up to 8,192 pixels), the chosen shape's area, or the annotations (unless bmxSave is cancelled).
exportAnnotations exportAnnotations(format?: "json" | "geojson") => Promise<string> The annotations as a file's text: the viewer's JSON or GeoJSON.
fit fit() => Promise<void> Fit the whole picture.
getAnnotations getAnnotations() => Promise<BmxZoomAnnotation[]> The annotations.
getInfo getInfo() => Promise<BmxDeepZoomInfo | null> What is open.
getView getView() => Promise<BmxDeepZoomViewState | null> The view: the picture point in the middle, the zoom, the turn, what shows.
loadSample loadSample() => Promise<BmxDeepZoomInfo | null> Open the built-in sample (the Mandelbrot set, a million pixels across, with a few named places marked).
open open(picture: Blob | string) => Promise<BmxDeepZoomInfo | null> Open a picture: a File or Blob (TIFF, JPEG, PNG, WebP…), or an address (also of a Deep Zoom, IIIF or Zoomify description), or sample.
openCompare openCompare(picture: Blob | string) => Promise<BmxDeepZoomInfo | null> Open a second picture to compare (side by side unless another way is set).
rotate rotate(degrees: number) => Promise<void> Turn the view by some degrees (clockwise).
select select(id: string | null) => Promise<void> Choose an annotation by id (null clears).
setAnnotations setAnnotations(value: unknown) => Promise<number> Replace the annotations (a list, its JSON, a saved file's JSON, or GeoJSON).
setView setView(view: { x?: number; y?: number; zoom?: number; rotation?: number; flip?: boolean; }, animate?: boolean) => Promise<void> Move the view: any of the middle point (picture pixels), zoom, turn and mirror.
snapshot snapshot(options?: BmxDeepZoomSnapshotOptions) => Promise<Blob> A picture of the view, or of a box of the picture at full detail.
zoomTo zoomTo(target: readonly [number, number, number, number] | { annotation: string; }) => Promise<boolean> Zoom to a box of the picture (pixels), or to an annotation by id.

Slots

SlotDescription
(default) Overlay elements, placed on the picture by data-x and data-y (picture pixels), sized by data-width and data-height when given.

CSS shadow parts

PartDescription
minimap The overview map.
panel The side panel.
stage The picture.
status The status line under the picture.
toolbar The toolbar.

CSS custom properties

PropertyDescription
--bmx-deep-zoom-height The viewer's height.
--bmx-deep-zoom-panel-width The side panel's width.
--bmx-deep-zoom-radius The frame's corner radius.
--bmx-deep-zoom-stage Behind the picture.