Skip to content

Visualizing Sample Collections

Developer Documentation

This page is intended for users who develop tailored visualizations using the GenomeSpy app.

Getting started

You can use the following HTML template to create a web page for your visualization. The template loads the app from a content delivery network and the visualization specification from a separate spec.json file placed in the same directory. See the getting started page for more information.

<!DOCTYPE html>
<html>
  <head>
    <title>GenomeSpy</title>
    <link
      rel="stylesheet"
      type="text/css"
      href="https://cdn.jsdelivr.net/npm/@genome-spy/app@1.3.0/dist/style.css"
    />
  </head>
  <body>
    <script type="module">
      import { embed } from "https://cdn.jsdelivr.net/npm/@genome-spy/app@1.3.0/dist/index.es.js";

      await embed(document.body, "spec.json");
    </script>
  </body>
</html>

For a complete example, check the website-examples repository on GitHub.

The App defaults to embedMode: "standalone", which manages URL state and bookmarks. When embedding in a host application that owns these, pass { embedMode: "embedded" } as the third argument to embed. Local interaction controls remain available.

When developing sample-collection visualizations locally, use the Inspector from the three-dot menu in the App toolbar to inspect the live view hierarchy, dataflow, params, and scale/axis/legend resolutions.

Schema-assisted editing

Follow the general schema-assisted editing instructions, but use the App schema: https://genomespy.app/schema/app/v<major>.json.

Specifying a Sample View

The GenomeSpy app extends the core library with a new view composition operator that allows visualization of multiple samples. In this context, a sample means a set of records representing an organism, a piece of tissue, a cell line, a single cell, etc. Each sample gets its own track in the visualization, and the behavior resembles the Core facet operator. Sample views are optimized for rendering and interacting with very large datasets, and can show sample-specific metadata alongside the tracks. For a simple repeated plot using only Core, see the genomic facet example.

A sample view is defined by the samples and spec properties. To assign a track for a row, define a sample-identifier field using the sample channel. More complex visualizations can be created using the layer operator. Unlike Core facets, sample views can partition a different data source for each layer using its sample channel. This enables concurrent visualization of multiple data types. For instance, the bottom layer could display segmented copy-number data, while the top layer might show single-nucleotide variants.

{
  "samples": {
    // Optional sample identifiers and label settings
    ...
  },
  "metadata": {
    // Optional metadata sources and metadata matrix layout
    ...
  },
  "spec": {
    // A single or layer specification
    ...,
    "encoding": {
      ...,
      // The sample channel identifies the track
      "sample": {
        "field": "sampleId"
      }
    }
  }
}

But we have Band scale?

Superficially similar results can be achieved by using the "band" scale on the y channel. However, you can not adjust the intra-band y-position, as the y channel is already reserved for assigning a band for a datum. On the other hand, with the band scale, the graphical marks can span multiple bands. You could, for example, draw lines between the bands.

Implicit sample identifiers

By default, GenomeSpy extracts sample identifiers from the field encoded with the sample channel, and each sample gets its own track.

Example

This example displays four segments for each of three samples. The required samples object is empty, so GenomeSpy derives the samples and their order from the sample field in the data.

{
  "description": "A simple segmented view of three samples.",

  "data": {
    "values": [
      { "sample": "A", "start": 0, "end": 15, "state": "neutral" },
      { "sample": "A", "start": 15, "end": 20, "state": "gain" },
      { "sample": "A", "start": 20, "end": 100, "state": "neutral" },
      { "sample": "B", "start": 0, "end": 25, "state": "neutral" },
      { "sample": "B", "start": 25, "end": 55, "state": "gain" },
      { "sample": "B", "start": 55, "end": 80, "state": "loss" },
      { "sample": "B", "start": 80, "end": 100, "state": "neutral" },
      { "sample": "C", "start": 0, "end": 30, "state": "gain" },
      { "sample": "C", "start": 30, "end": 60, "state": "neutral" },
      { "sample": "C", "start": 60, "end": 85, "state": "loss" },
      { "sample": "C", "start": 85, "end": 100, "state": "gain" }
    ]
  },

  "samples": {},

  "spec": {
    "mark": "rect",
    "encoding": {
      "sample": { "field": "sample" },
      "x": { "field": "start", "type": "index" },
      "x2": { "field": "end" },
      "color": {
        "field": "state",
        "type": "nominal",
        "scale": {
          "domain": ["loss", "neutral", "gain"],
          "range": ["#5090e0", "#e0e0e0", "#ff6040"]
        }
      }
    }
  }
}

Defining sample identity

Use samples.identity to define sample order, display names, or the complete sample set independently from the visualized data. Identity can come from explicit data or a configured metadata source.

Type: DataSampleIdentityDef | MetadataSampleIdentityDef | "metadata"

Identity from metadata

When a metadata source already defines the samples, reference its id:

{
  "samples": {
    "identity": {
      "metadataSource": "fetal-tissues",
      "displayNameField": "rowLabel"
    }
  },
  "metadata": {
    "sources": [{ "import": { "url": "metadata-figwig.json" } }]
  }
}

The backend supplies canonical IDs in source order. Figwig uses track names; Zarr uses matrix row IDs. Labels are optional and default to IDs. Table and Figwig sources can read labels from raw attributes even when those columns are excluded from displayed metadata. Zarr currently supports IDs as labels only.

With exactly one metadata source, the shorthand uses its IDs as labels:

"samples": { "identity": "metadata" }

Multiple sources require an explicit metadataSource. Identity loads before dependent signal queries and reuses the source's cached adapter for subsequent metadata imports. initialLoad: false defers attributes, while IDs are still read when the source is selected for identity. An empty source produces an empty sample view. IDs must be nonempty and unique; identity-loading errors fail initialization.

displayNameField

Type: string

Field in the source containing user-visible sample labels. It may be excluded from displayed metadata columns. Missing labels fall back to ids. The backend must support reading the configured label field.

If omitted, canonical sample ids are used.

metadataSource Required

Type: string

Id of the configured metadata source that defines the sample set and ordering. Canonical sample ids come from that backend, independently of which metadata columns are imported. For Figwig, ids are track names.

Identity from explicit data

Explicit sample identity
{
  "samples": {
    "identity": {
      "data": { "url": "samples.tsv" },
      "idField": "sample",
      "displayNameField": "displayName"
    }
  },
  ...
}

This configuration reads sample ids and display names from samples.tsv. Sample metadata, like clinical attributes, has to be provided separately in a metadata source.

The following properties configure samples.identity:

data Required

Type: UrlData | InlineData | NamedData | DynamicCallbackData | LazyData | Generator

Data source that defines the sample set for the view.

The source must contain one row per sample. Metadata imports are matched against these sample ids.

displayNameField

Type: string

Field containing a user-visible sample label.

If omitted, sample ids are used.

idField

Type: string

Field that contains the canonical sample id.

Default value: "sample"

The example below defines three samples and their display names with inline identity data.

{
  "description": "Three samples with display names defined by inline identity data.",

  "samples": {
    "identity": {
      "data": {
        "values": [
          { "sample": "S1", "displayName": "Control" },
          { "sample": "S2", "displayName": "Treatment A" },
          { "sample": "S3", "displayName": "Treatment B" }
        ]
      },
      "idField": "sample",
      "displayNameField": "displayName"
    }
  },

  "spec": {
    "view": { "fill": "#f8f8f8" },
    "data": { "values": [{}] },
    "mark": { "type": "text", "text": "Data" }
  }
}

Showing sample y-axes

Sample views can show vertical axes for the repeated sample plots when the child spec defines a y-axis. By default, the axis is repeated for every visible sample whose row is tall enough. Dense views hide the axes automatically to avoid clutter.

The sample view property that enables and disables this behavior is:

sampleYAxis

Type: SampleYAxisDef | null

Controls how vertical axes are shown for repeated sample plots.

Set to null to disable sample y-axes.

Default value: {"mode": "all", "minSampleHeight": 60}

When sampleYAxis is enabled, the following properties configure how sample y-axes are shown:

mode

Type: "all" | "top" | "middle" | "bottom"

How vertical axes are shown for repeated sample plots.

  • "all" shows an axis for every eligible visible sample.
  • "top", "middle", and "bottom" show one representative axis.

Default value: "all"

minSampleHeight

Type: number

Minimum visible sample height in pixels required before a repeated y-axis can be shown.

Default value: 60

Adjusting sample labels

The samples object controls sample labels. For example, to increase the sample label font size, use the following configuration:

Adjusting font sizes
{
  "samples": {
    ...,
    "labelFontSize": 12
  },
  ...
}

The following properties allow for fine-grained control of the font styles:

labelFont

Type: string

The font typeface. GenomeSpy uses SDF versions of Google Fonts. Check their availability at the A-Frame Fonts repository. System fonts are not supported.

Default value: "Lato"

labelFontSize

Type: number

The font size in pixels.

Default value: 11

labelFontWeight

Type: number | "thin" | "light" | "regular" | "normal" | "medium" | "bold" | "black"

The font weight. The following strings and numbers are valid values: "thin" (100), "light" (300), "regular" (400), "normal" (400), "medium" (500), "bold" (700), "black" (900)

Default value: "regular"

labelFontStyle

Type: "normal" | "italic"

The font style. Valid values: "normal" and "italic".

Default value: "normal"

labelAlign

Type: "left" | "center" | "right"

The horizontal alignment of the text. One of "left", "center", or "right".

Default value: "left"

In addition, the following sample label properties are supported:

labelTitle

Type: string | null

Title shown above sample labels. If omitted, the title defaults to "Sample". Set to null to hide the title.

labelLength

Type: number

Width reserved for sample labels in pixels.

If omitted, the width is inferred from the sample labels.

Sample row and group layout can be adjusted with the sampleLayout property:

sampleLayout

Type: SampleLayoutDef

Layout settings for sample rows and sample groups.

sampleHeight

Type: number

Height of one sample row when the view is expanded for (close-up) inspection. In the birdseye overview, sample rows are automatically scaled to fit the available vertical space.

Default value: 35

groupSpacing

Type: number

Spacing between sample groups in the fitted layout.

Default value: 5

peekGroupSpacing

Type: number

Spacing between sample groups in the expanded layout.

Default value: 15

sampleSpacingFactor

Type: number

Fraction of each sample row reserved as spacing between rendered sample contents.

Spacing is reduced when rows are too short to render cleanly.

Default value: 0.2

Metadata attribute label and matrix layout properties are documented in Configuring Metadata Sources.

For runtime parameters that respond to the sample layout and current sample set, see Sample View Parameters. For summary tracks across samples or sample groups, see Aggregating Samples.

Replacing sample collections

An App can start without a SampleView and attach one later through api.views. Keep a persistent concat with a fixed assembly and an explicit shared locus scale. Independent annotation siblings can stay in that concat while the SampleView is removed and recreated. App supports one active SampleView.

import { embed } from "@genome-spy/app";

const api = await embed(container, {
  name: "tracks",
  assembly: "hg38",
  scales: { x: { name: "locus", type: "locus", zoom: true } },
  resolve: { scale: { x: "shared" } },
  vconcat: [], // Independent annotation tracks can go here.
});
const tracks = api.views.get({ scope: [], view: "tracks" });
let cohort;

// The host supplies a SampleSpec compatible with the fixed assembly.
async function openCohort(nextSampleSpec, persistenceContext = null) {
  if (cohort) {
    await api.views.remove(cohort);
    cohort = undefined;
    // The host can now revoke blob URLs belonging to the removed cohort.
  }
  await api.setPersistenceContext(persistenceContext);
  cohort = await api.views.insert(tracks, nextSampleSpec);
}

Await each replacement before starting another. Removing a cohort clears its metadata, grouping, filters, selections, and undo/redo history. The operation is not undoable. Surviving annotations, their loaded data, and the current locus stay in place. If the next insertion fails, its partial resources are released and the App remains empty and ready for another insertion. Keep host-owned blob URLs valid until awaited removal or failed-insertion cleanup finishes.

setPersistenceContext changes source-specific bookmark identity without recreating App or changing cohort data. Pass null to disable source bookmarks, or provide a context such as:

await openCohort(nextSampleSpec, {
  specId: "dataset-b",
  baseUrl: "https://example.org/dataset-b/",
  remoteBookmarks: { url: "bookmarks.json" },
});

A remote bookmark URL must be absolute or have an explicit baseUrl. Relative bookmark content resolves from the remote bookmark file's directory. Detachment clears the previous source context; insertion does not configure persistence or automatically restore a bookmark.