Documentation · Tests & benchmarks
Tests & benchmarks
Test results, Apple M3 Max and NVIDIA RTX rendering times, and input data.
On this page
Release 0.15.0: 16 October 2026#
The source archive and wheel, built from a clean export of the release tree, passed twine check --strict. In a fresh Python 3.12 environment outside the checkout, the wheel installed cleanly and passed pip check and the installed-package check, which covers packaged resources, the CLI, the starter scene, and the skill; native and Studio movies rendered from it with every frame decoded. The full suite passed 366 tests, with six NVIDIA-only cases skipped, on the Apple M3 Max with PROTEINMOTION_TEST_EEVEE=1. The readability changes and the heatmap and density fixes left 180 stills from every gallery scene, with their coordinates, bit-for-bit identical. Across the remaining fixes, 348 stills from all 87 documentation and gallery scenes were unchanged; only the Write animations of the gallery distances clip and the RNA styles clip changed, and both clips were re-rendered. The website build, TypeScript checks, and all 146 exported pages (35,508 local links and media references) passed verification.
Release 0.14.0: 1 October 2026#
The source archive and wheel built from the release tree passed twine check --strict. In a fresh Python 3.12 environment outside the checkout, the wheel installed cleanly and passed pip check and the installed-package check, which covers packaged resources, the CLI, the starter scene, and the skill. Native and Studio movies of the starter scene rendered at 1280 × 720, and all 561 frames of each decoded. The full suite passed 319 tests, with five NVIDIA-only cases skipped, on the Apple M3 Max with PROTEINMOTION_TEST_EEVEE=1. The website build, TypeScript checks, and all 146 exported pages (35,546 local links and media references) passed verification.
Supersampling, rigid-core superposition, and per-atom fades: 1 October 2026#
- Supersampling. The native and Studio renderers now draw stills and movies up to 1920 × 1080 at twice the width and height and average each 2 × 2 block in linear light, on top of 4× MSAA. This removes stair-steps on thin tubes and callout lines, and the dotted seams where Studio's ambient occlusion, which reads one depth sample per pixel, met a thin tube in front of another surface. At 1080p the 1× and 2× frames of the adenylate kinase film differ by 0.2 gray levels on average (99th percentile 5): only edges change, because text, line widths, and effects are sized in 1080p design pixels. A test checks that 2× lies closer to a 4× reference than 1× does, for both renderers. The films rendered at 82 fps (adenylate kinase, previously 90), 31 fps (hemoglobin, previously 32), and 77 fps (torsions, previously 85).
- Per-atom fades in Studio. Atoms fading by their own opacity were drawn with order-independent transparency, which averages overlapping layers and never quite matches solid drawing: a tRNA region at opacity 0.996 differed from the same region at 1.0 by 0.51 gray levels on average (99th percentile 20). The end of every such fade therefore jumped, including the moment a morph hands over to its target. When all partly visible atoms share one opacity above 0.8, Studio now blends the transparent image toward solid layers (with the atoms solid, weighted by the opacity, and without them), reaching the solid image exactly at full opacity. A fade from 0.7 to 1.0 now changes by at most 0.06 per frame, including the last. Ghosts below 0.8 render as before. Fading geometry also casts ambient occlusion on solid surfaces in proportion to its opacity.
- Rigid core.
match_structuresfinds the matched Cα/C4′ atoms that superpose within 2 Å by repeatedly removing the worst tenth of pairs beyond 2 Å and refitting, as in ChimeraX matchmaker.StructureMorphanddomain_motionsuperpose on this core by default. It found 112 atoms (1.08 Å RMSD) for adenylate kinase, all in the CORE domain; 222 atoms (0.80 Å) for hemoglobin, 191 of them in α1β1; and 474 atoms (1.26 Å) for Cas9, mostly the nuclease lobe. In a synthetic test, a ubiquitin domain turned 40° about a hinge is measured at 40.1° with no fixed region given, and the unmoved half stays within 0.05 Å during the morph. - Sequence-aligned pairs. Aligned runs shorter than four residues between gaps are left unpaired. Between the Cas9 guide RNAs of 4ZT0 and 5F9R, which come from different constructs, this unpaired the GAAA tetraloop of 4ZT0, which alignment had matched to the tip of 5F9R's longer stem, and the last nucleotide, matched across a 16-nucleotide insertion. Both had flown about 30 Å across the morph.
- Cas9.
Cas9Morph(examples/structure_morph.py) morphs S. pyogenes Cas9 with guide RNA (PDB 4ZT0, chains A and B) to the R-loop complex (PDB 5F9R): 1,408 matched residues and 11,257 matched atoms, 13,652 atoms in the target. About the rigid core, HNH turns 117°, REC2 64°, REC3 18°, and REC1 4°. In 5F9R the HNH domain lies within 4.0 Å of the target strand, while catalytic His840 is still 13.9 Å from it, so the film says HNH rests against the strand rather than that it is docked for cleavage. The 19.5-second film rendered 1,170 frames at 1920 × 1080 and 60 fps in 52 s (22.5 fps), and every frame decoded. Its only one-frame steps above 20 gray levels come from the closing text and from the midpoint of the morph, where interpolation brings HNH backbone atoms within 0.65 Å of RuvC; both endpoints keep at least 3.1 Å.
The full suite passed 319 tests, with five NVIDIA-only cases skipped, with PROTEINMOTION_TEST_EEVEE=1. The five documentation previews added on this branch were re-rendered with supersampling and decoded completely. The website build, TypeScript checks, and all 146 exported pages (35,546 local links and media references) passed verification.
Cartoon orientation during motion: 1 October 2026#
Cartoons and ribbons were oriented by each residue's Cα→O vector, made consistent along the chain by flipping each one to match the previous residue. In helices that vector lies nearly along the Cα trace (cosine 0.7–0.97), so the remainder used as the ribbon's width axis was noisy, and neighbors sat close to the 90° point where the flip decision changes. As atoms moved, the decision changed and a stretch of ribbon turned by up to 87° in one frame. In the adenylate kinase morph (4AKE → 1AKE, 6 s at 60 fps), residue axes jumped by more than 8° between consecutive frames 78 times, including inside helices.
The width axis now comes only from Cα positions a few residues away: the local helix axis from consecutive Cα bisectors, curvature binormals for strands and loops, and the Cα→O direction only where the trace is straight. These are averaged as sign-free tensors. Each axis is then oriented like its predecessor turned by the typical twist, 49° per residue in helices and 15° elsewhere: the medians over 5,242 helix and 1,112 strand steps in 11 structures. Where a step is still ambiguous, the ribbon makes that turn through a short round section, so a change in the decision cannot flip a flat ribbon. Sheet arrowheads keep their strand's orientation. Keyframe interpolation now ends on the second keyframe's twist: per-atom alignment had reversed some segments, so the end of a backbone morph differed from the same structure drawn fresh.
- Motion. A numerical replay of the Studio cartoon sweep, sampled at 10 points per segment, counted width-axis jumps larger than 6° on the flat part of the cross-section. Before the change, the adenylate kinase and hemoglobin morphs had 586 and 367 such jumps in cartoon mode and 867 and 449 in ribbon mode. After it, both morphs have none in either mode. A new test checks that in the adenylate kinase morph every width axis turns less than 5° per frame and that segments reverse their twist only while round; the previous construction fails it with 87° jumps.
- Still structures. Across 1UBQ, 1PGA, 4AKE, 2DN2, 4HHB, 1AON, 1CLL, and AF-P0DP23-F1, rounding is visible (firmness below 0.7) on 0–2.6% of helix and strand steps, at kinks, bulges, and strand ends; a test requires above 98% to stay flat for four structures. In ribbon mode, short round sections also appear in loops (0–10% of loop steps).
- Films. Rendered films were scanned for one-frame pops: 12 × 12-pixel blocks, at 960 × 540, whose change in one frame exceeds their changes in the frames before and after. The largest such step fell from 115 to 15 gray levels in the adenylate kinase film and from 37 to 17 in the hemoglobin film, and neither film now has a step above 20, the level of text animation and moving plot points. In the torsions film the remaining steps above 20 are fast ball-and-stick motion and Ramachandran points; the hairpin's cartoon steps above 20 are gone. At 1920 × 1080 and 60 fps the adenylate kinase, hemoglobin, and torsions films rendered at 90, 32, and 85 fps; the vectorized orientation code is faster than the per-residue loop it replaces (70 and 25 fps for the morphs before). Still frames of 4AKE, 2DN2, 1UBQ, and the torsions film were compared with the previous version in cartoon and ribbon modes.
The full suite passed 313 tests, with five NVIDIA-only cases skipped, with PROTEINMOTION_TEST_EEVEE=1. The five documentation previews added on this branch were re-rendered and decoded completely. The website build, TypeScript checks, and all 146 exported pages (35,537 local links and media references) passed verification.
Structure morphs and assemblies: 1 October 2026#
StructureMorph was tested on human hemoglobin, from deoxy 2DN2 to oxy 2DN1 expanded to its biological assembly, and on E. coli adenylate kinase, from open 4AKE to closed 1AKE (chain A). Matching paired all four hemoglobin chains and hemes (574 residues, 4,516 atoms) and all 214 adenylate kinase residues (1,656 atoms). Both morphs end exactly on the target coordinates. At the halfway point, the largest bond-length change was 0.24 Å for hemoglobin and 0.10 Å for adenylate kinase, with 99% of bonds within 0.06 Å and 0.03 Å; linear interpolation of the same atoms changed bonds by up to 1.42 Å. domain_motion gives a 14.1° rotation of α2β2 relative to α1β1, and 52.2° (LID) and 44.8° (NMP-binding domain) relative to the adenylate kinase CORE. A synthetic test turns ubiquitin residues 40–76 by 40° about a hinge: halfway through the morph the domain is rigid to 0.02 Å and turned by 20°.
At 1920 × 1080 and 60 fps with the Studio renderer, the two example films (examples/structure_morph.py) rendered 1,062 frames at 25 fps (hemoglobin, including the live 574-residue distance heatmap) and 1,032 frames at 70 fps (adenylate kinase). Their 1280 × 720 gallery previews decoded completely, and review stills were inspected.
Torsions, secondary structure, heatmaps, and color presets: 1 October 2026#
The full suite passed 304 tests, with five NVIDIA-only cases skipped, on the Apple M3 Max with Python 3.12.8 and PROTEINMOTION_TEST_EEVEE=1. Twenty new tests cover the peptide builder, torsion measurement and animation, DSSP, Ramachandran plots, torsion markers, heatmaps, AlphaFold confidence files, and the hydropathy and conservation presets.
- DSSP. On 1UBQ, 1CLL, 4HHB, 1NCX, 2DRI, 1CFC, 1PGA, AlphaFold model AF-P0DP23-F1, and 1AON (9,595 residues), the assignment matched
mdtraj.compute_dsspreduced to helix (H, G, I), strand (E), and coil for every residue. - Torsions. Peptides built from φ/ψ/ω/χ angles reproduce them to 0.001°, with L-chirality at Cα (C–N–Cα–Cβ = −122.6°, the median of deposited structures) and closed proline, phenylalanine, histidine, and tryptophan rings. After
SetTorsions, φ and ψ match their targets to 0.001° and bond lengths are unchanged to 0.0001 Å. A torsion frame for a 17-residue peptide takes about 2 ms on the CPU. Seeking backward restores the start coordinates. - Ramachandran regions. One chain from each of 1,000 sequence-identity clusters (30%) of X-ray entries at 1.4 Å or better and R-free ≤ 0.20 gave 188,784 general, 18,999 glycine, 10,487 trans-proline, and 9,423 pre-proline residues after occupancy, alternate-location, and B-factor filters. With the matching backgrounds, all 74 ubiquitin residues with both φ and ψ lie in favored regions.
- Rendering. Native, Studio, and EEVEE frames change as the cartoon blends from coil to helix. The 61.6-second film
examples/torsions.pyexported 3,697 frames at 1920 × 1080 and 60 fps with the Studio renderer in 44 s (84 fps) with Metal and H.264 VideoToolbox. Its 1280 × 720 gallery preview (4 Mbit/s) and thetorsions(726 frames) andconfidence-and-conservation(1,050 frames) documentation previews decoded completely, and review stills were inspected for framing, label placement, and transitions.
The website build, TypeScript checks, and all 140 exported pages (32,627 local links and media references) passed verification.
EEVEE parity audit: 26 September 2026#
Every scene in the examples, 36 scenes at 75 moments, was rendered with the native renderer and with EEVEE at 480 × 270 and compared side by side. Representations, nucleotide base styles, residue colors and opacity, ligands and side chains, highlights, morphs, trajectories, density maps, plots, and text matched before the audit. The audit found six differences, all now fixed in EEVEE:
- Cutaways and depth tunnels were ignored. EEVEE now applies the native cone and tunnel formulas to exported geometry, fading cut faces in eighths of their opacity, and exports the tunnel wall and its depth rings as meshes.
- Wire tips had no glow. Glow halos now export as additive emission spheres with the native halo and core profile.
camera.depth_cuefog was missing. Materials now blend toward the background over the native distance range, with the factor converted from display to scene-linear blending.- The background was darker. Camera rays now see the scene-linear color that the view transform displays as the requested background; a test checks the corner pixel to within 2 levels.
- The backbone-morph demo stopped with 136 opacity layers. Opacities are now rounded to 1/48.
- 3D distance and interaction lines were drawn as a flat overlay over atoms. They now render as geometry, so atoms in front hide them; only their 2D parts stay in the overlay.
EEVEE's lighting remains its own: materials are brighter and more saturated, so very faint ghosted atoms are slightly more visible than in the native renderer. 203 tests pass, and with PROTEINMOTION_TEST_EEVEE=1 the EEVEE export and real-frame tests cover cutaways, tunnels, glow, fog, background color, and 3D rulers.
Cutaways, depth tunnels, and threading: 26 September 2026#
Version 0.12.0 passed 205 tests, with five NVIDIA-only cases skipped, on the Apple M3 Max with Python 3.12.8 and PROTEINMOTION_TEST_EEVEE=1. New tests check cutaway state and seeking, the tunnel axis staying fixed to the molecule while the camera moves, the tunnel ending before the first gap in the atoms along its centerline, the animated depth cue, and the view search. Threading tests check that each wire ends on its chain's trace atoms from N to C terminus with its backbone length, that Unthread mirrors Thread exactly, that stagger orders the chains, and that wire and glow colors follow tints and options. On the GPU, an open cutaway changes the frame, closing it restores the original pixels exactly, a tunnel differs from a cone window, and the glow pass only adds light.
Cutaways, tunnels, and the glow are drawn by the native renderer; EEVEE renders the full molecule and plain wires. The tunnel film (examples/depth_tunnels.py, 1AON) and the hemoglobin threading film (examples/thread_hemoglobin.py, 4HHB) were rendered at 1280 × 720 and 60 fps at 4 Mbit/s; all 3,865 and 1,386 frames decoded, and review stills were inspected. At 1080p, the 21-chain GroEL–GroES threading test rendered at 86–92 fps. The depth labels are computed from the structures: the ADP pocket of subunit A is 21 Å and the ring's inner wall 44 Å beneath the outer surface, measured straight in from outside the ring.
The wheel built from the source archive passed twine check --strict. A fresh Python 3.12 installation outside the checkout passed pip check and the installed-package check, including a rendered and decoded movie. The website build, TypeScript checks, and all 119 exported pages passed verification.
Ligands, ions, and side chains: 22 September 2026#
Version 0.11.0 passed 198 tests, with five NVIDIA-only cases skipped, on the Apple M3 Max with Python 3.12.8 and PROTEINMOTION_TEST_EEVEE=1. New tests use PDB 1CLL. They check residue categories, the ligand, ion, residue-name, and distance selectors against a direct distance calculation, side-chain atoms, staggered ShowSideChains timing, and seeking. Native renders of a finished animation and of the same static atom state are pixel-identical. Real Blender EEVEE frames change when ions and side chains are shown.
Cartoons now draw ligands, ions, and loaded waters by default, so scenes with such groups render differently from version 0.10.1. Existing gallery and documentation previews were not re-rendered.
The 41-second examples/binding_sites_film.py exported 2,460 frames at 1920 × 1080 and 60 fps in 12.6 s with Metal and H.264 VideoToolbox; every frame decoded. The wheel built from the source archive passed twine check --strict. A fresh Python 3.12 installation outside the checkout passed pip check and the installed-package check, including a rendered and decoded movie. The website build, TypeScript checks, and all 112 exported pages passed verification.
Four gallery films show the new capabilities: binding_sites_film.py (1CLL, 1AON), troponin_sites.py (1NCX), side_chain_ensemble.py (2K39), and nucleic_ions.py (1EHZ, 2DCG). Their 1280 × 720, 60 fps previews were encoded at 4 Mbit/s with H.264 VideoToolbox. All 2,460, 1,867, 1,578, and 1,794 frames decoded, and review stills were inspected for framing, label placement, and transitions.
PyPI release: 22 September 2026#
Version 0.10.1 passed 187 tests, with five NVIDIA-only cases skipped, on the Apple M3 Max with Python 3.12.8 and PROTEINMOTION_TEST_EEVEE=1. The checks include native Metal rendering, EEVEE, hardware export, and the Windows platform-selection logic. Blender discovery now searches the macOS application path only on macOS; encoder-fallback tests handle PyAV builds that omit NVENC.
The wheel built from the source archive passed twine check --strict. Fresh installations outside the checkout passed resource, CLI, starter-scene, and skill checks on Python 3.11.9, 3.12.8, 3.13.12, and 3.14.6. The Python 3.12 installation rendered and decoded all 60 frames of a 640×360 movie using Metal and H.264 VideoToolbox. A separate pipx installation exposed the CLI and passed GPU/encoder diagnostics.
The website build, TypeScript checks, and all 107 exported pages passed verification. The PyPI workflow passed actionlint. Its release run passed installation checks on Linux with Python 3.11–3.14 and on Windows and macOS with Python 3.12, then published the wheel and source archive through Trusted Publishing.
A fresh installation of version 0.10.1 from PyPI passed pip check, CLI, packaged-resource, starter-scene, and skill checks outside the source checkout. The downloaded wheel's SHA-256 matched the tested release artifact. On the Apple M3 Max, this installation rendered a 60-frame, 640×360 movie using Metal and H.264 VideoToolbox; every frame decoded successfully. Windows/NVIDIA hardware results from the previous version are recorded below.
Windows and NVIDIA: 18–19 September 2026#
Tested on Windows 11 (build 26200), NVIDIA GeForce RTX 5070 Ti, 16 GB VRAM, driver 595.97, and AMD Ryzen 7 9800X3D. Python 3.12.13, wgpu 0.32.0, PyAV 18.1.0, NumPy 2.5.3, Gemmi 0.7.5 and MDAnalysis 2.10.0 were used. The selected native adapter reports Vulkan.
190 tests passed, 2 skipped, with PROTEINMOTION_TEST_EEVEE=1, after integrating the v0.10.0 DNA/RNA changes. Skips are macOS-only HEVC VideoToolbox and a symlink test requiring Windows Developer Mode or administrator privileges. The suite covers rendering, surfaces, transparency, density, annotations, protein and nucleotide morph endpoints, reproducible seeking, NV12 padding and BT.709 conversion. Hardware exports test H.264, HEVC and AV1 NVENC, decode every frame, and verify dimensions, color metadata and presentation timestamps. Separate checks cover RGBA/NV12 input with unaligned dimensions and fractional frame rates, and hardware-failure fallback without overwriting an existing output.
An ordinary scene.render(output) call selects the platform, GPU backend and encoder at runtime. The GPU suite checks its reported choices and encoded output. A CLI export with no GPU or codec flags also selected Windows, the NVIDIA RTX 5070 Ti, Vulkan and h264_nvenc.
Two shader fixes were required on NVIDIA: snap interpolated opacity near 1 before dividing opaque and transparent passes, and return exact coordinates at interpolation endpoints. The previous shader failed eight rendering tests on this GPU; the existing assertions now pass without increasing their tolerances. Adapter selection prefers discrete hardware and Vulkan, with explicit GPU/backend overrides. Automatic export now selects NVENC on this machine.
Export benchmark#
The unmodified examples/quickstart.py scene exports 600 frames, 1920 × 1080, 60 fps, 4× MSAA. One warmup per codec precedes three interleaved runs. Timings include renderer startup, scene evaluation, GPU drawing, NV12 readback, encoding and file finalization. They exclude scene construction and validation. Other desktop applications remained running.
| Encoder | Median export time | Median throughput |
|---|---|---|
Previous Windows default: libx264, CRF 18, fast |
4.071 s | 147.4 fps |
New automatic choice: h264_nvenc, P4, HQ, VBR/CQ 18, 20 Mbit/s target |
1.920 s | 312.5 fps |
This is a 2.12× throughput improvement for this scene on this machine. Encoder quality scales and rate control differ; these are throughput measurements, not an equal-quality codec comparison. All 3,600 measured frames decoded with correct dimensions, BT.709 limited-range metadata and timestamps. Representative frames at 1, 4 and 9 seconds were visually inspected. Full measurements.
Reproduce from the repository in PowerShell:
.\.venv\Scripts\python.exe scripts/benchmark_export.py --repeats 3Packaging and Blender#
The source distribution and wheel build successfully. A wheel installed into a separate environment, run with isolated Python from outside the checkout, passed resource, starter-project and CLI checks and rendered a 60-frame movie using Vulkan/NVENC. Every frame decoded. No external FFmpeg executable or CUDA Toolkit was installed for these checks.
Windows discovery found Blender 5.2 under Program Files/Blender Foundation. Both optional EEVEE integration tests passed, including depth of field, overlays, opacity layers, all molecular representations, density and plots. The process runs without a console window.
Limits#
DirectX 12 produced device-loss errors in transparency passes with this driver/wgpu combination. Vulkan is the validated Windows path. Interactive preview, Linux, other Windows GPUs and Metal were not retested during this Windows run. Encoding still copies NV12 through CPU staging memory before NVENC upload. Earlier Apple silicon measurements below describe different scenes and hardware and should not be compared directly with this benchmark.
Earlier Apple silicon validation#
Tested on 16–17 September 2026 on the local Apple M3 Max, 40 GPU cores, 64 GB RAM,
macOS 26.6.2, arm64 Python 3.12.8. The native adapter reports backend_type=Metal.
wgpu 0.32.0, Gemmi 0.7.5, PyAV 18.1.0, NumPy 2.5.3, and MDAnalysis 2.10.0 were used.
Version 0.7: package installation and AI agent skill#
107 local tests pass. Six new CPU cases cover starter movies with paths containing spaces, rendering-scene construction outside the checkout, overwrite protection, skill installation under a configured Codex directory, idempotent installation, explicit updates preserving unrelated files, symlink rejection, and CLI entry points.
The wheel built from the source distribution was installed into a fresh environment with md and preview extras. An isolated Python process outside the checkout verified the installed module location, all shader/font/license resources, and the complete bundled skill. The installed CLI created a self-contained movie project and installed the skill to a temporary destination. A 60-frame, 640×360 native Metal/VideoToolbox movie rendered from the installed package and every frame decoded successfully. CI also runs the installed-resource and CLI checks.
The proteinmotion-movies skill passed the skill-creator validator and an independent authoring trial: a 10-second 640×360, 30 fps ubiquitin movie with residue selection, a representation transition and a Cα distance ruler. All 300 frames decoded. The trial identified a sizing ambiguity; the skill now explicitly documents 1080p design pixels for text, label offsets and 2D line widths. Skill guide · Install the release.
Version 0.6.3: continuous feature tour and stable fade endpoints#
The calmodulin and troponin C feature demo uses one camera and a single timeline: calmodulin stays on screen until the contact-guided morph, and the actual troponin C destination carries the subsequent interaction demonstrations. All 55 animation boundaries were checked for continuity of camera, visible atom coordinates, opacity, color and representation. Frames immediately before/after the styling-to-focus boundary at 21 seconds are pixel-identical at a ±1 μs interval. Backward seeking reproduces the same pixels, the troponin C pose is preserved after the morph, and the first/last frames meet on the same background for a gentle loop.
The GPU quintic easing function now mirrors its upper half, like the CPU implementation. This avoids floating-point cancellation near opacity 1 that could send apparently opaque cartoon fragments through the transparency pass and cause a brightness pop. Four new native GPU cases cover cartoon and ball-and-stick fades at 1×/4× MSAA and nonzero timeline offsets. All four fail against the previous shader and pass with the fix. 101 local tests pass.
The continuous film contains 6,030 frames / 100.5 seconds at 1920×1080, 60 fps and 4× MSAA. It exported in 23.54 seconds (256.2 fps) using Metal and VideoToolbox on the local M3 Max. This is one end-to-end run including renderer initialization, surfaces, readback and hardware encoding; caches may be warm. Its content differs from the earlier videos, so the timings describe separate workloads. The 720p web preview retains 60 fps. All frames of both encodes decoded successfully; representative frames and the transition around 20 seconds were inspected.
The MD-reader demonstration now uses 25 deposited 1CFC calmodulin NMR conformers, mapped by exact identity to all 1,130 protein heavy atoms in the displayed 1CLL topology. Six nonprotein atoms are hidden during playback. Atom identities and every XTC frame were compared with the mapped source, with maximum coordinate error 0.00501 Å (XTC quantization). The video interpolates slowly through states 1–3 to display NMR structural variation. Film, chapter guide and source · Measured render, continuity and input report.
Version 0.6.2: clean ball-and-stick fades#
Covalent cylinders are clipped at the analytic surfaces of their endpoint spheres in both opaque and transparent passes. The clipping uses the current GPU-interpolated positions and transformed atom radii. It removes internal sticks that became visible when the alpha-helix zoom started fading residues; bond-only annotation rulers keep their full endpoints.
97 local tests pass. Six added native GPU cases (1× and 4× MSAA) compare transparent atom cores with and without attached bonds, cover unequal atom sizes, transforms, global and residue opacity, overlapping/coincident endpoints, and ensure bond-only annotations retain their full lengths. The shaft between atoms remains visible.
The alpha-helix film and web preview were re-rendered. The 1,381-frame 1080p/60 fps export took 7.12 seconds (194.0 fps) on the local M3 Max using Metal and VideoToolbox; all full-resolution and preview frames decoded successfully. Frames around the 8.4-second fade were visually inspected. This is a single local run with potentially warm shader caches. Earlier timings used separate runs. Current render report.
Version 0.6.1: an alpha-helix hydrogen-bond diagnostic#
A fixed idealized 16-residue backbone with explicit amide H recovers exactly 12 of 12 expected O(i)···H–N(i+4) contacts, using the geometry criteria alone. The new endpoints="hydrogen_acceptor" option draws H···A and optionally labels that distance; the default donor–acceptor display remains available. The example separates the full network from an annotated single-bond close-up with distance labels beside the short dashed bonds.
Four added tests bring the suite to 91 passing tests. They independently check the fixture's φ/ψ angles, the complete pair set, a reversed-H negative control, an extended-backbone negative control, explicit/virtual endpoints, transformed anchors, native rendering and backward seeking. The existing 1UBQ test now checks six helix contacts at 150° and all eight at a declared 140° cutoff, documenting the two borderline angles rather than forcing them into the result.
The 1080p/60 fps Metal/VideoToolbox film contains 1,381 frames (23.02 seconds encoded); every frame decoded successfully. The original v0.6.1 export took 19.48 seconds, with other work running on the same machine. Test guide · Measured pairs · Render and geometry report.
Version 0.6: residue styling, surfaces and interactions#
The package now has 87 passing local tests. Twenty added cases cover independent residue color/opacity tracks, stagger timing and backward seeking, all four GPU representations, closed single-atom vdW/SAS/SES geometry and outward normals, surface caching/rebuilding, Coulomb energy/potential against analytic references, strict PQR charge mapping, hydrogen-bond distance/angle rejection, live ruler units and anchors, real NMR virtual backbone H, 3D occlusion versus 2D overlays at 1×/4× MSAA, and bounded interaction rendering through state changes.
Two films use 1920 × 1080, 60 fps, 4× MSAA, Metal and h264_videotoolbox:
| Example | Frames / duration | Export time | Throughput | Scene construction |
|---|---|---|---|---|
| Residue styling and rebuilt SES, 2K39 | 1,488 / 24.8 s | 33.695 s | 44.2 fps | 0.782 s |
| Hydrogen bonds, distances and charge contacts, 2K39 | 1,212 / 20.2 s | 8.455 s | 143.3 fps | 0.970 s |
These are single local end-to-end runs, including renderer setup, surface construction where needed, readback and hardware encoding, excluding scene construction/loading. Caches may be warm, and other processes were running on the machine. All 2,700 encoded frames decoded with the expected H.264 codec, dimensions, rate and frame count. Representative encoded color, transparency, surface and ruler frames were visually inspected. Raw timings · Video checks.
The surface uses a 1.4 Å probe and 0.5 Å voxel spacing. It rebuilds during coordinate changes, which dominates this export; static surfaces reuse GPU triangles for rotation and styling. The alternative fast deformation mode was visually unsuitable for large NMR conformer changes and is documented for small-displacement previews only. The default remains update="rebuild".
Hydrogen-bond examples use explicitly flagged virtual backbone H with D–A ≤ 3.5 Å and D–H–A ≥ 150°. Electrostatic examples use illustrative formal side-chain charges, dielectric 80 and screening length 8 Å. These checks compare the implemented geometry and formulas with their expected values and verify the rendered output. Methods and limits · Complete film source.
Scikit-image 0.26.0 was used for marching cubes. Its array-shape assignments emit NumPy 2.5 deprecation warnings in the mesh tests (55 warnings), alongside the two existing MDAnalysis warnings described below; all numerical and rendering assertions pass.
Version 0.5: text animation and labels#
Native text uses HarfBuzz shaping, FontTools outlines and cached triangle/stroke geometry. Write draws glyph contours and then fills them with Manim-style lagged timing; Unwrite reverses it. Callout and amino-acid label anchors follow selected coordinates through camera motion, deformation, NMR state interpolation and transforms.
Both new films use 1920 × 1080, 60 fps, 4× MSAA, Metal and h264_videotoolbox:
| Example | Frames / duration | Export time | Throughput |
|---|---|---|---|
| Protein labels and callouts, 2K39 | 1,128 / 18.8 s | 3.742 s | 301.5 fps |
| Close-up vector writing study | 600 / 10 s | 1.576 s | 380.7 fps |
These are single local runs including initialization, rendering, readback and hardware encoding, excluding scene construction/font layout; shader caches may be warm. All 1,728 encoded frames decoded successfully. Representative decoded frames were visually inspected. Raw export measurements and video verification.
Eleven new tests cover kerning/ligatures, Greek glyphs, empty text, contour holes/islands, automatic author-number labels, insertion codes, configurable writing/erasing, timeline conflicts, live anchors, viewport clipping, opacity inheritance, label placement and scaling. Native GPU checks run at 1×/4× sampling and verify contour-to-fill progression, glyph staggering/reversal, open letter counters, continuous fades, transparency composition, reproducible backward seeking and unchanged glyph buffers across frames.
Text is drawn over the protein image. The text guide describes supported fonts, placement options, and current limitations.
Version 0.4: regions and NMR playback#
The new example loads PDB 2K39, an RDC-derived solution NMR ensemble of ubiquitin. All 116 deposited models have matching selected atom identities: 602 heavy atoms, 76 residues, chain A. Conformers are rigidly aligned to the first model on the Cα atoms of residues 1–70. The original mmCIF is included, with its checksum and derived coordinate summary in the ensemble report.
The overview movies visit all 116 models in deposited order, using quintic easing between adjacent states to show structural variation. The region tour uses subsets of those states, first focusing on residues 23–34 with gold sphere/box annotations, then following residues 71–76 with cyan halos and a box.
All three movies use 1920 × 1080, 60 fps, 4× MSAA, native Metal, and
h264_videotoolbox with software encoding disabled:
| Example | Frames / movie duration | Export time | Export throughput | Scene construction |
|---|---|---|---|---|
| Region focus/highlight tour | 1,446 / 24.1 s | 4.521 s | 319.8 fps | 0.390 s |
| All 116 states, cartoon | 1,440 / 24 s | 3.694 s | 389.8 fps | 0.409 s |
| All 116 states, ball-and-stick | 1,440 / 24 s | 3.680 s | 391.3 fps | 0.386 s |
These are individual local runs including renderer initialization, rendering, readback, encoding, and finalization, but excluding scene construction/loading. Driver/shader caches may be warm. Every encoded frame (4,326 total) decoded successfully, with the expected dimensions, frame counts, H.264 codec, and 60 fps. Decoded helix/tail close-ups and ensemble frames were visually inspected. See timings and video checks.
New tests cover PDB-number selections, unions, invalid/empty selections, all three highlight styles through coordinate and transform changes, focus tracking with constant zoom, custom field of view, concurrent molecular motion, portrait framing, and backward seeking. Native GPU tests compare repeat renders after deformation and seeking. The renderer retains references to cached CPU arrays so Python object ID reuse cannot incorrectly skip a changed coordinate/metadata upload. Caching remains bounded. A fractional timeline-boundary regression checks quintic easing stays within [0, 1]. Real NMR tests verify every model is finite, selected exact model endpoints are preserved, and decoded-frame caching remains bounded.
Version 0.3: smooth fades#
Screen-door dithering has been removed. Fades use weighted blended order-independent transparency with an opaque depth pass, floating-point accumulation/transmittance targets, and a per-MSAA-sample composite. Opaque-only frames retain the single-pass path; extra targets and pipelines are allocated lazily on the first fade.
The calmodulin/troponin C examples were re-rendered at 1920 × 1080, 60 fps, 4× MSAA. Each contains 690 frames / 11.5 s. Native Metal plus VideoToolbox export measured:
| Representation | Export time | Export throughput | Scene construction |
|---|---|---|---|
| Ball-and-stick | 2.647 s | 260.7 fps | 0.024 s |
| Cartoon | 1.841 s | 374.8 fps | 0.022 s |
These are individual local runs, including renderer initialization and encoding, excluding loading/matching. Shader and driver caches may already be warm. Matching reuses the saved JSON. Every encoded frame in both movies decoded successfully. Representative decoded fade frames were inspected. See raw timing data and video checks.
New numerical GPU tests check five opacity levels against continuous alpha blending, opaque occlusion of a transparent atom, reversed transparent-object draw order, and single-surface bond fading. All run with both 1× and 4× sampling. The opaque fast path is checked before transparency allocation, and returning to full opacity reproduces the original frame. Transparent depth/color ordering remains an approximation; overlapping fragments use weighted blending.
Version 0.2: large structures and protein morphs#
All three new movies were rendered at 1920 × 1080, 60 fps, 4× MSAA through
native Metal and h264_videotoolbox with software encoding disabled:
| Workload | Selected heavy atoms | Cα residues | Video length | Export time | Export throughput |
|---|---|---|---|---|---|
| Calmodulin → troponin C | 2,414 across both inputs | 144 → 162 | 11.5 s / 690 frames | 2.863 s | 241.0 fps |
| GroEL chain A | 3,836 | 524 | 6 s / 360 frames | 1.216 s | 296.2 fps |
| Full GroEL/GroES, 21 chains | 58,870 | 8,015 | 9 s / 540 frames | 2.791 s | 193.5 fps |
These export times include renderer initialization, initial GPU geometry preparation, rendering, full-frame readback, hardware encoding, and file finalization. They exclude scene construction and loading. Construction took 0.021 s, 0.072 s, and 4.222 s, respectively; the GroEL complex preparation includes bond inference and orientation. The morph uses a saved correspondence, so matching is also excluded from export. Each measurement comes from a single local run.
The GroEL/GroES example uses actual 1AON coordinates and changes from a rotating cartoon to ball-and-stick. Counts exclude water and hydrogens. The 8,015 Cα atoms correspond to residues with coordinates in the deposited model.
The morph uses 1CLL chain A and 1NCX chain A. The saved mapping contains 114 monotone residue pairs, covering 79.17% of the source and 70.37% of the target. Contact parameters: cutoff 8 Å, softness 1.5 Å, maximum pairwise error 0.30. Observed RMS error: 0.0274571; maximum: 0.2936199. A proper rigid fit has Cα RMSD 7.0586 Å. The contact match therefore includes differences in domain arrangement.
The search considered 3,611 candidate pairings and stopped at its 15 s search budget (16.354 s including seed/graph preparation). The search returned a feasible match; optimality remains unproved. The known feasible count is 114; the conservative global count upper bound is 144. The 114 pairs are reproducible from the saved JSON rather than depending on wall-clock search termination. At a configurable 25 ms residue delay, motion proceeds from N to C; 30 unmatched source residues fade out and 48 unmatched target residues fade in. The morph lasts 6 s, with a 3.175 s movement interval per selected residue.
All 1,590 frames across the three encoded outputs were decoded successfully;
frame count, dimensions, H.264 codec, and 60 fps metadata match the requested outputs.
Representative decoded frames and the contact-map figure were visually inspected.
Raw records: timings, video checks.
Reproduce the scenes using examples/large_protein.py and examples/backbone_morph.py;
examples/contact_report.py exports a contact-map PDF/PNG and residue-pair CSV.
Version 0.2.1: calmodulin ball-and-stick morph#
BallAndStickDemo reuses the 114 saved Cα pairs, 25 ms delay, fade intervals, camera,
and 11.5 s timeline from the cartoon example. Both endpoints use element-colored
ball-and-stick, including their different side-chain atom sets. Bonds disappear with
their less-visible endpoint. The original cartoon example remains available.
The 690-frame, 1920 × 1080, 60 fps H.264 export took 2.626 s (262.7 fps) on the same M3 Max/Metal/VideoToolbox backend, with scene construction taking another 0.022 s. Matching is reused from JSON. This is one local measurement. Every encoded frame decoded successfully, and dimensions, frame rate and frame count match the request. A decoded midpoint frame was inspected. Raw records: timing and video validation.
Original v0.1 benchmark results#
At 1920 × 1080 with 4× MSAA:
| Workload | Atoms | Throughput |
|---|---|---|
| Warm cartoon rotation, render only | 602 | 1,222 fps |
| Warm ball-and-stick rotation, render only | 602 | 1,369 fps |
| Warm cartoon rotation, replicated scale test | 60,200 | 883 fps |
| Warm ball-and-stick rotation, replicated scale test | 60,200 | 188 fps |
| Complete showcase export, including encoding | 602 | 316 fps |
The original demo contains 1,230 frames / 20.5 seconds at 60 fps, H.264,
1920 × 1080, limited-range BT.709. Export took 3.898 seconds using
h264_videotoolbox with software encoding disabled. The scene includes all three
representations, rotations, camera movement, easing, morphing, deformation, and a
synthetic 61-state trajectory.
The render-only tests measure 180 frames after shader compilation and initial uploads; they include Python submission and synchronize completion with a final one-pixel readback. They exclude full-frame readback, encoding, file loading, and initial geometry preparation. The scale test repeats ubiquitin in a spatial grid inside one GPU object. It measures rendering with duplicated geometry. Cartoon rendering draws the resulting 7,600-residue backbone.
These are individual measurements on a machine with other workloads. Geometry, camera framing, image coverage, frame decoding, and system load affect the results. Raw timing data and examples/benchmark.py are included.
Export optimization#
An early RGBA-to-FFmpeg subprocess implementation measured only 21.4 fps for the same output dimensions. Profiling separated rendering/readback from encoding and IPC. The current exporter:
- Renders through native Metal.
- Converts the resolved RGBA texture to NV12 in a Metal compute shader.
- Reads back through three reusable staging buffers.
- Passes NV12 directly to PyAV's VideoToolbox encoder in the same process.
This reduces readback from 4 to 1.5 bytes per pixel and removes CPU color conversion and the raw-video pipe. It still copies data from mapped staging memory into the encoder. An IOSurface/CVPixelBuffer bridge could remove those copies in a future implementation.
Verification#
pytest: 107 passed for v0.7.0, including 101 from v0.6.3 and 97 from v0.6.2. Coverage includes:
- Real ubiquitin mmCIF loading, helix/sheet annotations, alternate-location selection, multi-model PDB loading, model identity mismatch rejection, and chain gap splitting.
- Rigid alignment precision and chirality, reordered atom correspondence for morphs, deformation endpoints, representation blending, easing endpoints, and conflicting animation channels.
- Deterministic forward/backward seeking, concurrent rotation and coordinate motion, memory-mapped trajectory units, reverse playback, and bounded frame caching.
- Writing and reading actual DCD and XTC files via MDAnalysis (coordinates generated for the test). The tests verify atom counts, ångström units, stride, and alignment.
- Native Metal rendering of cartoon, ribbon, and ball-and-stick; visible foreground, distinct representations, reproducible frames, and ordered triple-buffer readback.
- GPU NV12 conversion against a numerical BT.709 reference, including widths that require packed-word padding.
- H.264 hardware, HEVC hardware, and explicit libx264 fallback exports; frame counts, dimensions, codec and color metadata; preservation of existing output on failure.
- Interactive preview orbit/zoom/seek controls. A native preview window was opened, rendered, and closed successfully in a smoke test.
- Contact-matching cardinality and secondary error objective checked against an independent exhaustive enumerator on three small random systems; rigid-invariant matching with an insertion, proof flags, correspondence serialization, and chirality.
- N-to-C delay in seconds, per-residue endpoint accuracy with nonidentity transforms, complementary visibility, unmatched fade intervals, backward seeking, validation of timing/crossing correspondences, and subsequent ordinary morph behavior.
- Native GPU morph endpoint coverage and replay; stable endpoint/control buffer keys during staggered playback, confirming that frames reuse the geometry buffers.
- Real calmodulin/troponin C ball-and-stick morphs: whole-residue translations preserve internal atom offsets, all atoms inherit their residue's motion/fade timing, Cα paths coincide, and GPU endpoint images and backward seeks reproduce exactly.
- A GPU regression verifies that hiding one atom removes its whole connecting bond.
Two expected upstream MDAnalysis warnings occur: the announced DCD timestep behavior change and a missing time-step interval in the generated XTC fixture. Playback uses explicit scene duration, so neither changes the coordinate/frame checks.
Ruff checks and formatting pass. Source distribution and wheel builds are included; the packaged shaders are exercised by a wheel-install smoke check. The rendered gallery and representative encoded video frames were visually inspected.
Current limitations#
Rendering and hardware export are tested on Apple silicon Macs and Windows 11 with an NVIDIA RTX 5070 Ti. Other platform configurations remain untested. Backbone orientation guides are built on the CPU for each new state; large trajectories can be limited by this work or frame decoding.
Morphs interpolate coordinates, with residue order preserved in contact-guided matches. Bond lengths and atomic clashes can change during a morph. Ball-and-stick morphs move each residue as a group and crossfade the two atom sets. The matching report records whether the search completed and proved optimality.
Secondary structure stays fixed during playback. Prepare periodic boundaries before loading trajectories. Bond connectivity is estimated from geometry. Transparency uses approximate weighted blending.
See rendering, morphing, and trajectory preparation for the method details and supported inputs.
EEVEE and lens focus (v0.8.0)#
The full test suite passed 116 tests on Apple M3 Max, including native GPU tests and the optional EEVEE integration test. The EEVEE run used Blender 5.2 with Metal. CPU tests cover residue selectors, focus tracking, eased focus pulls with concurrent motion, arbitrary timeline seeks, and mesh export for all four representations.
The Blender test renders actual frames. It checks matching stationary frames, visible lighting, focus changes, annotation composition, camera projection alignment, representation changes, coordinate changes, surface rendering, empty scenes, and opacity-layer errors. Run it with PROTEINMOTION_TEST_EEVEE=1 pytest; Blender must be installed. Ordinary CI runs the CPU tests.
The EEVEE example renders an eight-second calmodulin sequence at 960×540 and 60 fps. All 480 frames are decoded after export. The source uses PDB 1CLL, chain A, and highlights residues 5–19. It includes a focus pull to residues 82–92 and back. The downloadable wheel is also checked outside the source checkout, including a Blender still render.
Calmodulin film and atom shading#
Calmodulin in focus was rendered with Blender 5.2 LTS on Metal at 1920×1080 and 60 fps. The 68-second scene uses 64 samples, 1.25× spatial supersampling, and a 28-pixel blur limit. All 4,080 frames in both the original export and the compressed web copy were decoded; dimensions and every presentation timestamp were checked. The camera and representation transitions were reviewed in rendered frames and motion samples.
The atomic close-up exposed reversed triangle winding in the EEVEE atom sphere mesh. The body triangles now face outward, matching their radial shading normals. A regression test checks the direction of every sphere face. The EEVEE tests passed with the Blender integration check enabled.
DNA/RNA and C1′ morphing (v0.10.0)#
The full suite passed 174 tests on Apple M3 Max with PROTEINMOTION_TEST_EEVEE=1 pytest -q. Blender 5.2 LTS used Metal. Lint and formatting checks passed. The wheel was installed outside the source checkout and checked with actual native and EEVEE renders.
New checks cover nucleotide classification, modified tRNA parent bases, C4′ traces, legacy prime/star atom names, base-mesh normals, residue appearance, surfaces, MD trajectory round trips, and deterministic GPU replay. C1′ matching is tested with deliberately conflicting C4′ coordinates. Morph checks verify C1′ alignment, ordered delays, whole-residue motion, complementary fades, missing-anchor errors, saved correspondences, and unchanged protein Cα report fields.
The DNA base-style video contains 1,393 frames; the tRNA video contains 870 frames; the DNA morph contains 732 frames. Each is 1280×720 at 60 fps, and every frame and presentation timestamp was checked after encoding. Native and EEVEE stills cover base styles, residue transparency, surfaces, and morph start/midpoint/end. An additional EEVEE DNA video contains 1,393 verified frames.
The morph uses chain A of PDB 1BNA and 2DCG. Five C1′ pairs match with a maximum soft-contact discrepancy of 0.16070 under the 0.20 bound. The completed full-candidate search proves the result for this configured objective. Seven source nucleotides fade out and one target nucleotide fades in. The 0.25-second delay gives a one-second start-time span and four seconds of motion per matched nucleotide in the five-second transition.
DNA/RNA matching uses one selected strand per endpoint. Cartoon traces use C4′/P; morph anchors use C1′. Morph interpolation preserves each endpoint residue’s internal geometry while crossfading, and does not constrain inter-residue bonds or calculate a physical pathway.