How to Make Publication-Quality Figures in PyMOL: Protein-Ligand Complexes, Step by Step - StemSkills Lab
Skip to content

How to Make Publication-Quality Figures in PyMOL: Protein-Ligand Complexes, Step by Step

How to Make Publication-Quality Figures in PyMOL: Protein-Ligand Complexes, Step by Step

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.

EditionCostLicensePublication figures?Catch
Open-Source PyMOLFreeBSD-likeYesYou install it yourself (conda or a build); no vendor support
Educational PyMOLFreeEducational-use onlyNoClassroom and homework only; publishing is explicitly excluded
Incentive / Academic PyMOLPaid subscriptionCommercialYesAdds 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 MK1

remove 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, polymer

hide 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 lig

The 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, 4

The 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, 0

A 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, 1

Two 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.

Join the waitlist (free) →

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=1

That 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.

SettingValue to useWhat it does
ray_trace_mode1“normal color + black outline”. Outlines survive shrinking to column width, which is why the figure still reads on paper
antialias2Smooths edge stair-stepping during the ray trace
ray_shadows0Turns off ray-traced shadows, which mostly add ambiguity about depth in a flat figure
ray_trace_colorblackSets the outline colour drawn by ray_trace_mode
cartoon_side_chain_helper1Stops cartoon and backbone sticks being drawn over each other
cartoon_fancy_helices1Gives 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. Run count_atoms lig: if it returns 0, your residue name is wrong. Check it with iterate_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 to util.cbay.
  • The PNG has a black background in Word or PowerPoint. You rendered with ray_opaque_background, 0 and the transparent alpha channel is being composited onto black. Set it to 1 and re-render.
  • The exported image is small and blurry. You called png with no dimensions, so it used the viewport size. Pass width= and dpi= explicitly.
  • The ray command appears frozen. Expected behaviour at large sizes, per the official docs. Test your layout with ray_trace_mode, 0 at 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=2 works 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 mode at its default. Mode 0 is “all interatomic distances”; you want mode=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.

Join the waitlist (free) →

Think you know Protein–Protein Modeling?
Take the free StemSkills assessment and earn a verifiable certificate you can download and add to your LinkedIn profile.
Start the free assessment

Keep going

How to Handle Metal Ions in Molecular Docking and MD (Zinc, Magnesium and Iron, Step by Step) Stop deleting the zinc. Learn when to keep a metal ion, how to dock into a metalloprotein with… How to Set Protonation States and Add Hydrogens to a Protein Before Docking or MD (PDB2PQR, PROPKA and H++) Crystal structures ship without hydrogens. Learn to predict pKa values with PROPKA, protonate with PDB2PQR or H++, and… How to Run GROMACS on an HPC Cluster with SLURM: Job Scripts, Modules and Restarts Move your GROMACS run from a laptop to a shared cluster. Load the right module, write a correct…
See live workshops