Embedding and driving chromosomix
An iframe
Section titled “An iframe”<iframe src="https://chromosomix.app/?embed=1#uri=https://rsglab-data.s3.eu-west-3.amazonaws.com/demo/yeast/Yeast_microC_G2M_1kb.cool&view=region®ion=IV:1000000-1400000&transform=log10&balance=true" width="100%" height="640" loading="lazy" style="border: 0" title="Micro-C of yeast chromosome IV"></iframe>- The
srcis a link to a view, as the links page writes one, with?embed=1before the#. Or?embed=1&config=<URL>for a saved workspace — several tabs, their tracks, colours and marks. - The frame is the view alone: no header, no tab strip, no status bar. The Annotations and Aesthetics panels are there, folded, because they are controls over the picture; the Files, General and Analysis panels are not. Add
&panels=opento the query to open them both (panels=annotationsorpanels=aestheticsfor one). A reader can still drop a file of their own on it. - The view fills the frame and follows its size, so a frame the reader can resize is a box away: see A frame the reader can resize, below.
- For a figure that must not change, pin a release:
https://chromosomix.app/v/<tag>/?embed=1#…. The root is the newest build, and changes with it. - Nothing is needed of the page around it — no
Cross-Origin-Opener-Policy, noCross-Origin-Embedder-Policy— and nothing of the data host beyond what any link needs: the frame’s requests come from the viewer’s origin, so a bucket whose CORS admits the viewer admits every page that embeds it. - An embed writes nothing to the reader’s storage and adds nothing to their back button: it shows what its
srcsays, every time it loads.
A frame the reader can resize
Section titled “A frame the reader can resize”Put the frame in a box the browser lets the reader resize by its corner. The padding keeps the corner’s grip clear of the frame, which would cover it, and the script lets the pointer through the frame while the box is dragged, without which the drag stalls as soon as it shrinks the box under the hand:
<div class="chromosomix-frame" style="resize: both; overflow: hidden; width: 100%; height: 640px; padding-bottom: 14px; box-sizing: border-box"> <iframe src="https://chromosomix.app/?embed=1#uri=https://rsglab-data.s3.eu-west-3.amazonaws.com/demo/yeast/Yeast_microC_G2M_1kb.cool&view=region®ion=IV:1000000-1400000&transform=log10&balance=true" style="display: block; width: 100%; height: 100%; border: 0" loading="lazy" title="Micro-C of yeast chromosome IV"></iframe></div><script> for (const box of document.querySelectorAll('.chromosomix-frame')) { box.addEventListener('pointerdown', (event) => { if (event.target === box) box.querySelector('iframe').style.pointerEvents = 'none' }) } addEventListener('pointerup', () => { for (const frame of document.querySelectorAll('.chromosomix-frame iframe')) frame.style.pointerEvents = '' })</script>The channel
Section titled “The channel”Six messages, each an object with a type. Protocol version 1; a message is added without changing the number, so a host reads which messages a frame takes from its ready.
| Message | Direction | Fields | What it is |
|---|---|---|---|
chromosomix:ready |
frame → page | protocol, version, commit, accepts |
Sent once, when the frame is listening. accepts lists the messages it takes. It carries nothing about what is on screen. |
chromosomix:where |
page → frame | — | Where is the view? Answered with chromosomix:state. |
chromosomix:navigate |
page → frame | state |
Go here. state is a patch laid over the view on screen: any key a link carries, so { locus: "IV:1,000,001-1,100,000" } moves a 1D browser and { transform: "log2", oe: true } re-colours a map. A key given null is removed; state: null is the landing page. Answered with chromosomix:state, saying where the view actually went. |
chromosomix:add |
page → frame | files |
Put these addresses in the tab, in order, as the app’s + does: a track among the tracks, a map or a 3D model beside the map; on an empty tab the first is its file. http(s) only, at most 64. Waits for the first picture, and is answered with chromosomix:state once every file is in. Builds before 0.17.0 do not take it. |
chromosomix:state |
frame → page | state |
The view: an object of the keys a link carries (uri, view, region, locus…), or null for the landing page. Sent in answer, and on every move once the page has spoken. |
chromosomix:rendered |
frame → page | report |
A picture is finished: report.status is drawn, gated (waiting for the reader to agree to a large read) or error, with report.maps (each map’s title, binsize, resolution, rows, cols), report.lanes and report.state, the view as a hash string. What to take a screenshot on. |
- The page speaks first. The frame answers only the window that framed it, and only once that window has sent it something;
chromosomix:readyis the one message it sends unasked. Listen before the frame loads — put the script ahead of the<iframe>, or set itssrcfrom the script — so as not to miss it. - Replies go to the origin the page spoke from, never to
*: the reader may open their own file in the frame, and only the page that asked is told. - Messages are carried out in the order they came. A
navigatesent after anaddwaits for the files to open, and is laid over the view they opened. - A patch is read as a link is: a key the frame does not know is dropped, a value it cannot read is ignored. Read the answer to know where the view went.
A host page
Section titled “A host page”const frame = document.querySelector("iframe")const viewer = new URL(frame.src).originconst send = (message) => frame.contentWindow.postMessage(message, viewer)
window.addEventListener("message", (event) => { if (event.source !== frame.contentWindow) return const message = event.data if (message?.type === "chromosomix:ready") { if (message.accepts?.includes("chromosomix:add")) { send({ type: "chromosomix:add", files: ["https://rsglab-data.s3.eu-west-3.amazonaws.com/demo/yeast/Yeast_ATAC.bw"] }) } send({ type: "chromosomix:navigate", state: { region: "IV:1100000-1300000" } }) } if (message?.type === "chromosomix:state") console.log("the view is at", message.state) if (message?.type === "chromosomix:rendered") console.log("drawn:", message.report.status)})In a Jupyter notebook
Section titled “In a Jupyter notebook”The same frame as an anywidget, in JupyterLab, Notebook 7, VS Code, Colab and marimo. A file is an address or a path on the kernel’s disk, which the kernel serves to the frame by byte range from 127.0.0.1, sidecars and all; nothing is uploaded.
# pip install chromosomixfrom chromosomix import Chromosomixview = Chromosomix("sample.mcool::/resolutions/5000", region="chr2:10000000-14000000", tracks=["ATAC.bw", "genes.gtf.gz"], transform="log10")view # the map, its tracks, its side panelsview.goto("chr2:11000000-12000000") # a region, or a locus in the 1D browserview.navigate(transform="log2", oe=True) # any key a link carries; None removes oneview.add("model.g3d.gz") # a track, a map or a 3D model beside the mapview.state # where the reader is: a traitlet to observeview.rendered # the last finished pictureviewer="0.17.0" pins the build in the frame to a release; chromosomix.install_viewer() fetches one to serve beside the data, which is what makes local files work behind JupyterHub and offline.
In R: R Markdown, Quarto and Shiny
Section titled “In R: R Markdown, Quarto and Shiny”remotes::install_github("js2264/chromosomix", subdir = "widgets/r")
library(chromosomix)chromosomix("sample.mcool::/resolutions/5000", region = "chr2:10000000-14000000", tracks = c("ATAC.bw", "genes.gtf.gz"), transform = "log10") |> chromosomix_goto("chr2:11000000-12000000") |> chromosomix_navigate(oe = TRUE) |> chromosomix_add("model.g3d.gz")In Shiny, chromosomixOutput(), renderChromosomix() and chromosomixProxy() drive a view on screen, and input$<id>_state follows the reader. A page knitted for others names its files by address: a local file is served only while the R session that rendered the page runs.
Documentation of chromosomix v0.20.0 · built from 40ec1ec · source
