Blog
How to Make Publication-Quality Figures in PyMOL: Protein-Ligand Complexes, Step by Step
- July 17, 2026
- Posted by: Stem Skills Lab
- Category: Molecular Modeling

A publication-quality PyMOL figure is four decisions, not one command: a white background, a stripped representation (cartoon protein, stick ligand), polar contacts drawn with distance in mode=2, and a ray-traced export sized for print with png fig.png, width=13.2cm, dpi=300, ray=1. PyMOL’s defaults are tuned for the screen, not for a journal page.
The last mile of a docking project is the part nobody teaches. You spend three weeks on preparation, scoring and rescoring, then paste a grey screenshot into your thesis and lose marks for it. The gap between that screenshot and a figure a reviewer takes seriously is about fifteen commands. This guide from the StemSkills Lab team (10+ years in sequence and structural bioinformatics, drug discovery and design, and multiscale molecular modeling) walks every one of them against a real structure, and answers the licensing question that catches students on day one. It sits inside our pillar guide on learning molecular docking.
Why do default PyMOL screenshots look unpublishable?
Because the default viewport is a preview, not a render. Three things give it away instantly:
- The black background. Fine on a monitor, wrong on paper, and it eats toner in a printed thesis.
- Everything is shown at once. Waters, ions, crystallographic neighbours and every side chain compete with the thing you are trying to point at.
- It is not ray traced. The interactive display uses fast OpenGL shading. Enlarge that screenshot to column width and the edges break up into visible steps.
A figure fails when a reader cannot tell in two seconds what they are meant to look at. Every command below either removes a distraction or sharpens the subject. Nothing else is decoration.
Which version of PyMOL should you install, and is it free?
This trips up more students than any rendering setting, and getting it wrong can affect your paper. PyMOL is maintained by Schrödinger, and as pymol.org puts it: “PyMOL is a commercial product, but we make most of its source code freely available under a permissive license.” That source lives at the pymol-open-source repository, which is “Published under a BSD-like license”.
Here is the part that matters. Schrödinger also offers a free Educational build, and students reach for it first because registration takes a minute. But the educational build page states plainly that “it is not provided for the purposes of academic research or publication.” The educational FAQ is blunter still. Asked whether it can be used for publishing, the answer is: “No. We consider publishing to be a professional task which is beyond education.”
So the build most students install is the one build they may not use for the figure in their paper. Read that twice before your submission deadline.
| Edition | Cost | License | Publication figures? | Catch |
|---|---|---|---|---|
| Open-Source PyMOL | Free | BSD-like | Yes | You install it yourself (conda or a build); no vendor support |
| Educational PyMOL | Free | Educational-use only | No | Classroom and homework only; publishing is explicitly excluded |
| Incentive / Academic PyMOL | Paid subscription | Commercial | Yes | Adds support and some features (see below) |
The practical answer for a BSc or MSc student writing a thesis: install Open-Source PyMOL. It is free, the license permits publication, and it runs every command in this article. The clearest functional gap is in the distance command, where modes 5 to 8 (pi-pi and pi-cation interactions) are Incentive-only, as the distance documentation notes. Polar contacts, the ones you actually need here, work everywhere.
What is the minimum command sequence for a protein-ligand figure?
We will use PDB entry 1HSG, HIV-1 protease with the inhibitor indinavir (ligand code MK1). It is the structure most docking tutorials use, so you can follow along with your own docked pose substituted in. For context on how much material is available to practise on, the RCSB PDB held 256,840 released structures as of 2026.
Start clean. Paste these into the PyMOL command line one block at a time:
fetch 1hsg, async=0
remove solvent
remove not polymer and not resn MK1remove solvent deletes the waters. The second line drops ions and buffer components that are not your protein or your ligand. If a structural water mediates the binding you care about, keep it deliberately (select keep_wat, resn HOH within 3.5 of resn MK1) rather than keeping all of them by accident.
Now set the canvas and strip the representation back to nothing:
bg_color white
hide everything
show cartoon, polymer
color grey80, polymerhide everything is the command that does the most work in this article. You are building the figure up from an empty scene instead of trying to subtract clutter from a full one. The protein is grey on purpose: it is context, not subject.
Next, the subject:
select lig, resn MK1
show sticks, lig
util.cbay ligThe util.cba* family colours atoms by element while letting you choose the carbon colour. As the Advanced Coloring documentation states: “The util.cba* (‘Color By Atom’) commands color atoms according to type: oxygen in red, nitrogen in blue, hydrogen in white.” The suffix picks the carbons: util.cbay yellow, util.cbag green, util.cbac cyan, util.cbaw white or grey.
Do not reach for util.cbaw here. It is a common recommendation, but it gives you white carbons, and you have just set a white background. Your ligand skeleton disappears into the page and only the red and blue heteroatoms survive. On white, use util.cbay or util.cbag. Save util.cbaw for a dark background or for a second protein-coloured copy of the ligand.
Finally, aim the camera at the binding site rather than the whole complex:
orient lig
zoom lig, 4The 4 is a buffer in angstroms. Widen it to show more pocket, tighten it to fill the frame with the ligand.
How do you show hydrogen bonds without the figure turning into spaghetti?
Use the distance command in polar-contact mode, then turn off its labels. The documented syntax is distance [ name [, selection1 [, selection2 [, cutoff [, mode ]]]]], and the mode argument is what keeps the figure readable. Mode 0 gives “all interatomic distances”, which is the spaghetti. Mode 2 gives “only show polar contact distances”, which is what you want:
distance hbonds, lig, polymer, 3.5, mode=2
color black, hbonds
hide labels, hbonds
set dash_width, 2.5
set dash_gap, 0.35
set dash_round_ends, 0A 3.5 angstrom cutoff is the conventional working definition of a hydrogen bond between heavy atoms, and it is a number you should be able to defend if asked. The labels come off because the distances printed in the viewport are unreadable at print size; if a specific distance matters, state it in the caption where it can be typeset properly.
Then show only the residues that make those contacts, not every side chain in the pocket:
select site, byres (polymer within 4.5 of lig)
show sticks, site and not (name C+N+O)
util.cbaw site
set cartoon_side_chain_helper, 1Two details there earn their place. and not (name C+N+O) hides the backbone atoms of those residues so you see side chains rather than a thicket. And cartoon_side_chain_helper stops PyMOL drawing the cartoon ribbon and the backbone sticks on top of each other, which is the double-line artefact you have probably seen in a labmate’s poster. Here util.cbaw on the pocket residues is correct: grey carbons read as context and keep the yellow ligand dominant.
Want the guided, hands-on version?
Our live Molecular Modeling & MD Simulations cohort bootcamp takes you from zero to running real docking and MD workflows, with a portfolio project for your grad-school applications.
How do you render at a resolution a journal will accept?
Stop guessing pixel counts and work backwards from the journal’s specification. PLOS ONE’s figure guidelines ask you to “Submit figures at the desired dimensions with a resolution no greater than 300-600 dpi”, set the maximum width at 7.5 inches (19.05 cm), and note that to align a figure with the text column you should “make it no wider than 5.2 inches (13.2 cm)”.
PyMOL’s png command accepts physical units, so you can type that specification in directly instead of computing pixels. The documented syntax is png filename[, width[, height[, dpi[, ray[, quiet]]]]], and width accepts pixels, inches (in) or centimetres (cm):
set ray_opaque_background, 1
png hiv_protease_indinavir.png, width=13.2cm, dpi=300, ray=1That single line renders a ray-traced, single-column figure at 300 dpi. The ray=1 argument runs the ray tracer first; without it you export the fast viewport image and lose the point of the exercise.
Be patient with it. The ray documentation warns that “The ray command can take some time (up to several minutes, depending on image complexity and size).” That is normal, not a crash.
Two practical notes. First, ray_opaque_background controls transparency: as the documentation explains, “if this option is ON then the background is whatever you specify… however, if the setting is OFF, then the background will be treated as a transparent alpha channel.” Set it to 0 for a slide deck where you want the ligand floating on your own background, and 1 for a manuscript. Second, PLOS ONE accepts “TIFF or EPS only”, and PyMOL writes PNG, so convert at the end (ImageMagick’s convert, or any image editor). Convert once, at the final step, never mid-workflow.
Which settings separate a deliberate figure from a default one?
These are the render settings worth setting explicitly. Add them before the png line.
| Setting | Value to use | What it does |
|---|---|---|
ray_trace_mode | 1 | “normal color + black outline”. Outlines survive shrinking to column width, which is why the figure still reads on paper |
antialias | 2 | Smooths edge stair-stepping during the ray trace |
ray_shadows | 0 | Turns off ray-traced shadows, which mostly add ambiguity about depth in a flat figure |
ray_trace_color | black | Sets the outline colour drawn by ray_trace_mode |
cartoon_side_chain_helper | 1 | Stops cartoon and backbone sticks being drawn over each other |
cartoon_fancy_helices | 1 | Gives helices defined edges instead of flat ribbons |
The ray_trace_mode documentation lists four values: 0 normal colour, 1 “normal color + black outline”, 2 “black outline only”, and 3 “quantized color + black outline”. Mode 1 is the one for manuscripts. Mode 3 produces a “cartoony appearance” and the docs warn it “sort of burns the background”, so it is a poster choice, not a paper choice.
Put the whole thing in a .pml script rather than typing it live. You will need to regenerate the figure after review, and a script means the second version matches the first exactly. It also sidesteps a memory issue: the ray docs recommend rendering “from a script rather than a PyMOL session file” when memory is tight, because sessions need copies in RAM simultaneously.
Troubleshooting: real errors and their fixes
- Your ligand vanished after
hide everything. The selection is empty. Runcount_atoms lig: if it returns 0, your residue name is wrong. Check it withiterate_state 1, hetatm, print(resn)and use the code you actually see. - The ligand sticks are there but invisible on the white page. You used
util.cbaw. White carbons on a white background. Switch toutil.cbay. - The PNG has a black background in Word or PowerPoint. You rendered with
ray_opaque_background, 0and the transparent alpha channel is being composited onto black. Set it to1and re-render. - The exported image is small and blurry. You called
pngwith no dimensions, so it used the viewport size. Passwidth=anddpi=explicitly. - The
raycommand appears frozen. Expected behaviour at large sizes, per the official docs. Test your layout withray_trace_mode, 0at a small size, and only run the full render once the scene is final. - Your H-bond dashes connect to hydrogens that are not there. Most crystal structures have no hydrogens.
mode=2works on heavy-atom donor and acceptor geometry, which is why the 3.5 angstrom cutoff is measured between heavy atoms. - Every distance in the pocket is drawn. You left
modeat its default. Mode 0 is “all interatomic distances”; you wantmode=2.
How should you cite PyMOL?
Every figure you publish needs the tool credited in your methods. PyMOL has no single canonical paper, so the recommended form given on pymol.org/citing is a software citation: “The PyMOL Molecular Graphics System, Version 3.0 Schrödinger, LLC.” Edit the version number to the one you actually used, which you can print with print(cmd.get_version()). If you used the open-source build, say so.
Frequently asked questions
Is open-source PyMOL missing features I need for a thesis figure?
Not for anything in this guide. Cartoons, sticks, surfaces, polar contacts, ray tracing and PNG export are all in the open-source build. The visible gap is distance modes 5 to 8 (pi-pi and pi-cation), which are Incentive-only, and vendor support.
Can I use the free Educational PyMOL for my paper?
No. Schrödinger’s educational FAQ answers this directly: “No. We consider publishing to be a professional task which is beyond education.” Use the open-source build or an Academic subscription for anything you intend to publish.
What dpi should I export at?
300 dpi at the final printed width covers most journals; PLOS ONE asks for no greater than 300-600 dpi. Set the width in centimetres and let PyMOL compute the pixels, rather than rendering a huge image and scaling it down afterwards.
Should I use a surface or a cartoon for a docked pose?
Cartoon plus sticks for showing specific interactions, because a surface hides the residues making them. A surface is better for one job: showing pocket shape and burial. If you need both, make them two panels rather than one crowded image.
Why does my figure look different every time I make it?
Because you are clicking and dragging. Save the camera with get_view, paste the output into your .pml script as set_view (...), and every future render is identical.
Can I render a figure from a docked pose that is not in the PDB?
Yes. Load your receptor and your docked ligand (load receptor.pdb then load out.pdbqt or an SDF of the pose), then run the same sequence. The only change is that your ligand is its own object, so select lig, out replaces the resn MK1 selection.
Figures are a skill with a learning curve like any other, and they are the part of your work an examiner sees first. If you want to know where visualization sits alongside docking, MD and the rest, our computational biology skills roadmap lays out the order we teach these in and why.
Want the guided, hands-on version?
Our live Molecular Modeling & MD Simulations cohort bootcamp takes you from zero to running real docking and MD workflows, with a portfolio project for your grad-school applications.