Skip to content

Instance, Events, and Export

The object returned by embed controls the lifetime of the embedded GenomeSpy instance and exposes a few instance-level utilities.

Finalizing

Call finalize() when the embedded visualization is no longer needed. It releases GenomeSpy resources, unregisters event listeners, and removes the created DOM content from the embed container.

const api = await embed(container, spec);

// Later, when the host component is being destroyed:
api.finalize();

Data loading

Data sources can fail independently while the rest of the visualization remains usable. dataLoading exposes those outcomes without reading the visualization or browser console. It observes eager URL and lazy loading, including transform side inputs. It is separate from datasets: named-data updates throw to their callers (or reject their parsing promise), and application-owned fetches remain the application's responsibility.

const api = await embed(container, spec);
const sources = new Map();
const unsubscribe = api.dataLoading.subscribe((change) => {
  if (change.type === "update") {
    sources.set(change.entry.sourceId, change.entry);
  } else {
    sources.delete(change.sourceId);
  }
});
for (const entry of api.dataLoading.getSnapshot()) {
  sources.set(entry.sourceId, entry);
}

Subscribe and read the snapshot synchronously, with no await between them. The snapshot includes eager failures preceding embed completion; notifications report one future source update or removal. Each detached entry has a stable sourceId, a status ("loading", "complete", or "error"), and an error message when available. A new loading attempt replaces the previous error. Unrequested hidden views and future lazy windows have no certified outcome.

Equivalent declarations may share one source and one entry. viewId and viewPath describe its original declaring view, even if that view is later removed. The optional getSpecOrigin(fragment) embed option receives each data definition and can return its location, such as a JSON Pointer. Reports include it as origin, so an editor can locate a failed definition. Return undefined when the location is unknown. A shared source reports one definition's origin. errorPhase identifies confirmed request or processing failures and is absent when attribution is ambiguous.

Located processing errors also include errorLocation, identifying the encoding or expression declaration that failed. Its origin and relative path follow the specification error location contract. The entry's own origin continues to identify the data definition.

Unsubscribe when observation is no longer needed. Finalization removes all subscriptions; subsequent reads or subscriptions throw. Listener exceptions are reported asynchronously to the browser and do not change source outcomes.

Waiting for visible lazy data

For automated validation or capture, awaitVisibleLazyData(signal) waits for the current visible lazy data and rejects when a required source fails. Check the snapshot as well to detect eager failures in branches without lazy dependencies:

await api.awaitVisibleLazyData();
const failures = api.dataLoading
  .getSnapshot()
  .filter((entry) => entry.status === "error");
if (failures.length) {
  throw new Error(failures.map((entry) => entry.message).join("\n"));
}

Handle the rejected promise in your caller. An already recorded relevant failure rejects immediately without retrying. The wait ignores unrelated hidden sources and accepts an AbortSignal for cancellation.

Interaction events

An embed exposes native canvas input through events.subscribe(). The listener runs synchronously before Core's default action and receives canvas CSS-pixel coordinates. Call preventViewDefault() during the callback to veto that action; browser cancellation still belongs to sourceEvent. Call sourceEvent.preventDefault() before any asynchronous work.

const stopContextMenu = api.events.subscribe("contextmenu", (event) => {
  if (brush.contains(event.point)) {
    event.sourceEvent.preventDefault();
    event.preventViewDefault();
  }
});

The supported native event names are click, dblclick, contextmenu, mousedown, mouseup, mousemove, mouseenter, mouseleave, and wheel. Unsubscribe when the host no longer needs the listener. Mark-scoped activation, hover, and explicit picking are available on ViewHandle.marks; see View hierarchy.

addEventListener() and removeEventListener() attach listeners for interaction events emitted by GenomeSpy. Currently, only "click" events are supported. The event object includes datum, the underlying datum for the clicked mark instance.

Deprecated API

The instance-level interaction event API is deprecated and will be removed in a future version. Use api.events.subscribe() or ViewHandle.marks.

const listener = (event) => {
  console.log(event.datum);
};

api.addEventListener("click", listener);

// Later:
api.removeEventListener("click", listener);

Exporting raster images

imageExport.raster() exports the current visualization as a PNG Blob.

const { blob } = await api.imageExport.raster();

The default logical dimensions match the current canvas, and pixelRatio defaults to window.devicePixelRatio. The visualization background is used when configured; otherwise exports use white. The background option overrides this default; set it to a color or null for transparency:

const { blob } = await api.imageExport.raster({
  logicalWidth: 1200,
  logicalHeight: 600,
  pixelRatio: 2,
  background: null,
  mimeType: "image/png",
});

image/png is currently the only supported MIME type.

Raster export first uses the active rendering backend. If that backend does not support raster export, GenomeSpy tries a detached Canvas2D surface without initializing another GPU renderer. Export rejects when neither is available. Native-font and effect limitations of the fallback are the same as in the live Canvas2D renderer.

Deprecated canvas export

exportCanvas(width, height, devicePixelRatio, background) remains available for compatibility and returns a PNG data URL. Use imageExport.raster() for new code.

Exporting SVG

imageExport.svg() exports the current visualization as an SVG Blob. Views become nested SVG groups, and supported marks, axes, legends, and titles remain editable vector elements.

const { blob, warnings, rasterized } = await api.imageExport.svg();

The default dimensions match the current canvas. The visualization background is used when configured; otherwise exports use white. The background option overrides this default; set it to a color or null for transparency:

const result = await api.imageExport.svg({
  logicalWidth: 1200,
  logicalHeight: 600,
  background: null,
});

The result contains:

  • blob: the serialized SVG with MIME type image/svg+xml.
  • warnings: unsupported visual properties that were ignored. These warnings do not prevent the rest of the visualization from being exported.
  • rasterized: descriptions of mark layers embedded as raster images.

Exported text uses the configured font followed by a list of system-font fallbacks. The exact appearance can vary when the configured font is not available in the application that opens the SVG.

SVG export loads requested browser fonts before measuring and generating text. Fitting, squeezing, and sequence-logo placement therefore follow the SVG destination font. Data transforms are not rerun, so cross-renderer exports may retain filtering and spacing decisions made using the live renderer's metrics.

Downloading the SVG

Use the returned Blob with the browser's download APIs or pass it directly to another file or upload API:

const { blob, warnings } = await api.imageExport.svg();
warnings.forEach((warning) => console.warn(warning));

const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "visualization.svg";
document.body.appendChild(link);
link.click();
link.remove();

// Revoke the URL after the browser has started the download.
setTimeout(() => URL.revokeObjectURL(url), 1000);

Rasterizing dense layers

Large mark layers can make SVG files slow to open and edit. The optional rasterization setting embeds a mark as a transparent PNG when its visible, post-culling instance count exceeds maxVectorInstances. Adjacent rasterized layers are combined into the same image when possible, while axes, labels, and other layers remain vectors.

const result = await api.imageExport.svg({
  rasterization: {
    maxVectorInstances: 5000,
    pixelRatio: 2,
  },
});

pixelRatio controls the resolution of embedded images and defaults to 2. Higher values produce sharper raster layers and larger files. It does not change the SVG dimensions or vector elements.

Rasterization first uses the active renderer when it supports selective rasterization. Otherwise, GenomeSpy tries a detached Canvas2D surface without initializing another GPU renderer. If neither is available, export remains functional and emits vectors instead, with a warning in the result. Omitting rasterization produces a vector-only SVG and does not load a raster backend.

Previewing rasterization

imageExport.analyzeSvg() reports the visible instance count of each mark layer without creating an SVG or using a raster backend. It can be used to preview which layers would cross a rasterization threshold:

const { layers } = await api.imageExport.analyzeSvg();
const threshold = 5000;

const rasterizedLayers = layers.filter(
  (layer) => layer.instanceCount > threshold
);

Each layer reports its view name, optional resolved view title, hierarchy path, mark type, and instance count. The optional logicalWidth and logicalHeight settings use the same CSS-pixel coordinate system as imageExport.svg().