py2Dmol, embedded

One script tag and one call. Each section below is a complete program — the code shown is the code that made the viewer beside it, run once when the page loaded — and each introduces exactly one thing the one before it did not. Every viewer is live: drag to rotate, scroll to zoom.

1 · One call

Ubiquitin, 1UBQ. A C-alpha trace is enough — the backbone, the secondary structure and the ribbon are all worked out from it.


    

The div is yours, the canvas inside it is the library's. PDB or mmCIF, told apart by looking at the text, so you do not have to say which you have.

The frame around the canvas is on by default. box: false takes it off.

Getting the text. show takes a string, so it is yours to fetch, read from a file input, or write into the page. For the two public archives there is a helper that knows the URLs:

const text = await py2Dmol.fetch('1UBQ');    // four characters: the PDB
const text = await py2Dmol.fetch('P01308');  // anything else: AlphaFold
py2Dmol.show('mol', text);

It is separate from show and hands back text rather than a viewer, so a download that fails — offline, a wrong id, a slow server — is yours to catch, and show stays something that either works or throws immediately.

Biological assemblies are built by default. A crystal structure file holds the asymmetric unit, which for anything symmetric is a fraction of the real molecule — one protomer of a trimer, a sixtieth of a capsid — along with the operations that make the rest. Those are applied unless you say otherwise:

py2Dmol.show('mol', text, { biounit: false });   // the deposited coordinates

A file with no assembly records is drawn exactly as it is, which is most files, so the default costs a search that finds nothing.

2 · A style

Four looks, one list. tube is the backbone trace; the other three are cartoons.


    

Pass it to show as style, or change it later with setStyle. Both take the same four names.

3d comes up on a black page, since solid shaded geometry is meant to be seen on one. bg: 'white' overrides that.

3 · A colour mode

Haemoglobin, 4HHB — four chains, so the modes differ visibly. auto chooses for you: one chain gets rainbow, several get chain.


    

auto · chain · rainbow · plddt · deepmind · ss. A name it does not know throws, rather than leaving the picture unchanged and letting you wonder.

plddt reads the B-factor column, which in a crystal structure is a real B-factor rather than a confidence.

4 · ...on part of it

A mode colours everything. Give the colour a selector and it colours part of it instead — and an explicit colour always beats the mode.


    

This is the selector, and everything else on the page takes the same one. A bare string is a chain, {range} is half open, and {residues} is the numbering in the file.

Colours accumulate: a range and then a chain leaves both in place. To start again, set a mode.

5 · Selecting

A selection is a highlight — it changes nothing about what is drawn. Two verbs, each relative to what is selected now.


    

select adds, unselect takes away, and unselect() with nothing to take away clears.

Clicking the canvas selects too: a click takes a residue, shift-click adds another, double-click takes the whole chain, and a click on the background clears. select: false turns that off for a viewer meant only to be looked at.

6 · Hiding

Different from selecting: this decides what is drawn at all. The same two shapes — add and take away, relative to what is on screen.


    

hide takes residues off, show puts them back, and resetVisibility() returns to everything. To draw one thing and nothing else, reset first and hide the rest: v.resetVisibility(); v.hide({not: sel});

What is hidden is hidden to everything else too: it cannot be clicked, and it does not pull the camera when you orient.

7 · A selector that computes

Trypsin with benzamidine in its pocket. Two selector keys that find residues rather than naming them, so nothing below mentions a residue number.


    

{type} is what a position is: 'L' ligand, 'P' protein, 'N' nucleic. You do not need to know what the ligand is called.

{near} is everything within 5 Å of another selector, measured atom to atom. That is a contact shell — a hydrogen bond is under 3.5 and a salt bridge under 4. {angstroms} changes it.

The ligand is part of site, since it is within 5 Å of itself. {not: {type: 'L'}} would leave it out.

8 · Side chains

The same site, with atoms. Side chains are drawn per residue, so you ask for the ones you want to see.


    

hideSidechains takes them away again.

They arrive coloured by element — carbon in the residue's own colour, oxygen red, nitrogen blue — and so does a ligand. That is on by default, since those are the atoms with an element worth reading; v.hideElements(sel) turns it off for a flat colour, and v.showElements(sel) puts it back.

This needs a structure that carries side-chain atoms — a C-alpha trace has none, and asking for them throws rather than quietly doing nothing. The file here keeps every C-alpha, and all the atoms of the residues nearest the ligand.

9 · The camera

A viewer turns each structure to face you as it loads. orient is for afterwards, and it takes a selector, so it can frame a part.


    

With no selector it frames the selection, or everything visible if nothing is selected.

{animate: false} jumps instead of flying. On load it never animates, and orient: false keeps the file's own orientation for a structure that already sits the way you want it.

10 · Clipping in

Close up, the pocket is still behind the front of the protein. A slab cuts down to it.


    

The slab is as thick as the selection is deep, and refits as you turn, so it stays over the site rather than at a fixed depth. Drag it and watch.

To cut deeper, clip to less: v.clip({type: 'L'}) keeps a thinner slice than v.clip(site). There is no separate thickness setting — the selector is it.

v.clip() with no selector turns it off.

11 · More than one frame

An NMR ensemble, 1YNE — six models, and RNA, so this is the nucleic acid example too. Nothing below asks for either.


    

A file with more than one model gets a player; a single structure stays a bare canvas. play: false refuses it, play: true insists.

The player is the library's: play, a slider, a counter and a speed control. You can also step frames from code with v.setFrame(i).

Bases are drawn as plates on the backbone. Nothing asks for that either — a structure holding protein and nucleic is drawn as both.

12 · Contacts

Lines between residues. The weight sets the width, and the colour is optional.


    

Within one structure an end is either a position or a chain and residue number: [5, 60, 1.0] or ['A', 12, 'B', 34, 1.0].

An empty list clears them. Contacts belong to the structure, so loading over it replaces them.

13 · Two structures at once

load puts a second structure up under its own name, and showObjects draws both. Here the second is ubiquitin again, moved 26 Å clear so the two are easy to tell apart.


    

Names are yours to choose. They are deliberately not A and B here, which is what the chains inside a structure are called.

The camera widens to take in a structure it has not framed before. Hiding one and showing it again leaves the camera where it is.

A contact can join two different structures. Each end names its own, and the contact belongs to the viewer rather than to either one, so you pass no name.

14 · Two viewers on one page

Call show twice with different elements.


    

The canvas is looked up inside its own container, so the containers are what must be distinct. Everything the library creates inside them is scoped and may repeat.

The two viewers are independent: a selection, a style or a camera in one does not touch the other.

15 · The panel and the player

Ask for controls and you get the notebook's own panel — the same one, not an imitation.


    

Style, Capture and every slider, plus Orient and Rotate. Off by default: most embeds want a picture and nothing else.

Its styling is scoped to your container, so it cannot reach your page's own dropdowns or buttons.

It stays inside the box you give it. On a fixed height the panel scrolls; in a container too narrow for the panel beside the canvas, it wraps underneath.

Which bundle

Two, differing only in which painter they carry. Everything on this page runs the same on either.

filesize
py2Dmol.embed.min.js449 KBWebGL2. What this page loads. Turning and zooming cost one draw call, which is what makes a large structure usable.
py2Dmol.embed.cpu.min.js410 KBThe 2D painter. 46 KB smaller, no WebGL2 needed, and it can export SVG — but a big structure repaints slowly.

Both carry the control panel and the capture UI — what controls: true gives you. Neither carries the sequence, PAE or MSA panels, saved sessions, or structural alignment; for those, use the full application.

Neither is a fallback for the other. Each carries one painter and nothing behind it, so the WebGL2 build on a browser without WebGL2 draws nothing and says so on the console rather than quietly changing how it works. There is no gpu option for the same reason: the file you load decides.

On a 313,000-atom capsid the difference is 26 ms a frame against 840. Below a few thousand residues you will not notice it, and the CPU build is the only one that can save an SVG.

Reference

py2Dmol.show(target, text, options)

Creates the canvas inside target (an element or its id), parses text, and returns the viewer. The format is decided by looking at the text — mmCIF if it declares data_, PDB otherwise — so a caller who fetched a URL need not know which they got.

const v = py2Dmol.show('mol', text, {
    width: 300,          // defaults to the element's own width
    height: 300,
    style: 'richardson', // tube (default), richardson, ribbon, 3d
    color: 'chain',      // auto, chain, rainbow, plddt, deepmind, ss
    bg: 'white',         // 'black' is the default under style '3d'
    box: true,           // the frame around the canvas
    name: 'structure',   // what to call this object
    biounit: true,       // build the biological assembly, if the file says how
    orient: true,        // turn it to face the reader as it lands
    select: true,        // clicking the canvas picks a residue
    controls: false,     // the Style panel
    play: undefined,     // the frame strip; on by itself when frames > 1
});

Anything else is passed to the viewer as configuration: detail (2–8 segments per residue), shadow, shadow_strength, outline, ortho, colorblind, ss_palette, rotate, autoplay, detect_cyclic. There is no gpu option — the bundle decides which painter draws, and passing one throws.

py2Dmol.fetch(id)

A structure by id, as text: four characters go to the PDB, anything else to AlphaFold. Returns a promise; a bad id or a lost network rejects, which is why it is not folded into show.

The selector

One grammar, taken by everything that names a set of residues.

'B'a chain, by id
[3, 4, 5]positions outright; a Set works too
{chain}one id or several
{positions}indices into what is drawn, from zero
{range: [a, b]}positions a..b-1, half open
{residues}the numbering in the file
{type}'L' ligand, 'P' protein, 'N' nucleic
{near}within 5 Å of another selector, atom to atom
{angstroms}...at another distance
{object}within one object of several
{not}the complement

Keys narrow each other, so {chain: 'B', residues: [12]} is one residue of one chain. A key it does not recognise throws — a selector that narrows nothing would mean everything, which is rarely what a typo was meant to say.

Positions are not residue numbers. A position counts from zero across everything drawn; a residue is the number in the file, which starts where the depositor started and can repeat between chains. Both are integers, so nothing will warn you if you pass one for the other.

Looking

v.setStyle(name)tube, richardson, ribbon, 3d
v.setColor(colour, sel?)a mode for everything, or a colour on part of it. Colours accumulate until you set a mode again
v.orient(sel?)frame that, or the selection, or everything visible. {animate: false} jumps
v.clip(sel)a slab as deep as the selection is, refitted as you turn. No argument turns it off
v.render(reason)redraw; everything above already does

Choosing what is drawn

v.select(sel) · v.unselect(sel)add to and take from the highlight, both relative to it. v.unselect() clears
v.hide(sel) · v.show(sel)subtract from and add to what is showing, both relative to it
v.resetVisibility()draw everything again. Reset and then hide({not: sel}) draws sel alone
v.showSidechains(sel) · v.hideSidechains(sel)side-chain atoms, per residue. Throws on a structure that carries none, such as a C-alpha trace
v.showElements(sel) · v.hideElements(sel)colour those atoms by element. On by default wherever there are atoms to read — ligands and drawn side chains — so this is mostly how you turn it off
v.setVisibility(patch) · v.getVisibility()the lower level underneath those, taking Sets of what to keep. Prefer hide and show

Structures and frames

v.load(text, name)another structure, under its own name. Loading over a name replaces what was there
v.showObjects(names)which structures are drawn. The camera widens for one it has not framed before
v.setFrame(i) · v.addFrame(frame, name)step to a frame, or add one. Frames come from py2Dmol.frameFromText, or framesFromText for every model in a file
v.addObject(name)an empty object to add frames to
v.resetAll()back to an empty viewer

Contacts

v.setContacts(list, name?) — lines between residues, an empty list clears. Within one object an end is a position or a chain-and-residue pair:

v.setContacts([[5, 60, 1.0],
               [10, 40, 0.4, { r: 255, g: 0, b: 0 }]]);
v.setContacts([['A', 12, 'B', 34, 1.0]]);

An end written as a selector naming one residue may sit in a different structure. Such a contact belongs to the viewer rather than to either object, so pass no name:

v.setContacts([[{ object: 'ubiquitin', residues: [10] },
                { object: 'copy', residues: [40] }, 1.0]]);