Screenshot a live view (@jbrowse/capture)
Drive a live JBrowse 2 instance with Puppeteer and screenshot it once it has actually finished rendering.
npx @jbrowse/capture --hub hg38 --loc BRCA1 \
--track hg38-ncbiRefSeqCurated --track hg38-phyloP100way -o brca1.png
--hub names an assembly on genomes.jbrowse.org,
which hosts a ready-made JBrowse config per UCSC and GenArk genome, and --loc
takes a gene name because those configs ship a text index.
Knowing when JBrowse has finished rendering
Knowing when a genome browser is done is the entire problem. JBrowse loads its config, builds a session, resolves an assembly, fetches each track, and then draws to a canvas — and a screenshot taken at any point before the last step is a picture of an empty browser that looks like a successful run.
Every readiness signal JBrowse publishes is negative: no loading overlay, no
display in its loading phase, no unpainted canvas. All of them therefore pass on
a page whose JavaScript has not started yet. Measured against
jbrowse.org/code/jb2/latest:
networkidle2resolves at ~350ms- the session appears at ~880ms
- the assembly and tracks land at ~2500ms
- the loading overlay only goes up after that
A wait chain built from those signals alone finishes in under a second and reports success.
So this package puts a positive gate in front of them, read off the live MST
session model that jbrowse-web publishes as window.JBrowseSession: the session
exists, its views are initialized, and the assembly and trackIds you asked for
are the ones actually open. A config URL that 404s, a trackId the config does
not define, and an assembly name that does not match all fail there, loudly.
Library
import { captureJBrowse, openJBrowse } from '@jbrowse/capture'
// one call: launch, wait, shoot, close
const { pending, paintContract } = await captureJBrowse({
hub: 'hg38',
loc: 'BRCA1',
tracks: ['hg38-ncbiRefSeqCurated'],
out: 'brca1.png',
})
// or keep the page, to click things and read state back
const { browser, page } = await openJBrowse({ hub: 'mm39', loc: 'Sox2' })
const tracks = await page.evaluate(
() => window.JBrowseSession.views[0].tracks.length,
)
await browser.close()
Two waits, depending on what you did:
waitForJBrowseReady(page)is the wait on its own, for a page you navigated yourself. The individual stages (waitForSession,waitForLoadingComplete,waitForDisplaysDone,waitForQuiescent, ...) are exported too, and each one documents what it can and cannot tell you.waitForAppSettled(page)is the wait after you CLICK something. A page that is loading starts outloadingand the transition intoreadyis it finishing; a page you just clicked is alreadyreadyand stays that way until the click's work registers, so waiting forreadythere returns on the pre-click frame.waitForAppSettledrequires it to hold.
Timeouts and unsettled waits
Puppeteer waits are usually written .catch(() => {}) so a slow page is not
failed for being slow. The cost is that "everything settled" and "we gave up"
become the same void — the run ends with an image and an exit code of 0 either
way, which is the vacuous-gate problem again, one step later.
Here every stage reports its outcome, and an unsettled one throws by default, naming the gate:
gave up waiting after 2000ms: the loading overlay never cleared (a track fetch
never finished). Raise the timeout if the page is merely slow; if it never
finishes, open the same URL in a browser — this gate has no content to fall
through to.
allowUnsettled (--allowUnsettled) takes the frame as it stands instead, and
still tells you what did not settle.
Reading the result
Three fields on a successful capture:
unsettled— stages that hit their timeout. Empty unless you asked to proceed anyway.pending— displays still reporting unpainted when the shutter fired. A display can return to pending after its stage passed, so this is a separate question fromunsettled.paintContract— whether this JBrowse build publishes the per-display paint attributes at all. It does not on the current released build, which is what every genomes.jbrowse.org link opens; there,pending: []means "cannot tell", not "all done", and the CLI says so. A page with no tracks open reports true — there is nothing to measure, which is not the same as being unable to.
CLI
jb2capture --help for the full list. Also:
jb2capture list hg38 conservation # trackIds matching a filter
jb2capture url --hub hg38 --loc BRCA1 # just print the link, no browser
See also
- @jbrowse/img renders SVG/PNG with no browser at all, via server-side React. Prefer it for a static figure; use this when you need the real app — canvas and WebGPU rendering, dialogs, menus, or state read back out of a running session.
- Using JBrowse with AI agents