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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Two, differing only in which painter they carry. Everything on this page runs the same on either.
| file | size | |
|---|---|---|
| py2Dmol.embed.min.js | 449 KB | WebGL2. What this page loads. Turning and zooming cost one draw call, which is what makes a large structure usable. |
| py2Dmol.embed.cpu.min.js | 410 KB | The 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.
gpu option for the same reason: the file you load
decides.
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.
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.
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.
| 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 |
| 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 |
| 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 |
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]]);