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.
metadataSourceRequired-
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¶
{
"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:
dataRequired-
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
nullto 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:
{
"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 tonullto 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.