DEV Community

Cover image for Stepping Inside H5N1: A Structural Biologist's Personal VR Journey From Headline to Binding Pocket
Ashish
Ashish

Posted on

Stepping Inside H5N1: A Structural Biologist's Personal VR Journey From Headline to Binding Pocket

Stepping Inside H5N1: A Structural Biologist's Personal VR Journey From Headline to Binding Pocket

The first time I saw the headline — “H5N1 avian flu jumps to mammals, new HA mutations raise pandemic risk” — my coffee went cold. I’m not a virologist; I’m a structural biologist who spends most days staring at electron‑density maps on a 27‑inch monitor, rotating a protein with a mouse, hoping the side‑chain rotamers line up. The news felt like a personal challenge: could I actually see the mutated receptor‑binding pocket the way the virus sees it?

I had a spare weekend, a Quest 2 headset gathering dust, and a half‑finished Unity project that loads PDB files into an OpenXR scene. The plan was simple: pull the latest HA trimer (PDB 7XYZ, deposited the same week), parse the asymmetric unit with a Python script, stream the coordinates to the headset, and let me walk around the trimer in 6‑DoF. No marketing brochure, no demo video — just code, a headset, and a question.

# fetch_and_prepare.py
import requests, gemmi, json, sys

pdb_id = "7XYZ"
url = f"https://files.rcsb.org/download/{pdb_id}.pdb"
resp = requests.get(url, timeout=10)
resp.raise_for_status()
structure = gemmi.read_structure_from_string(resp.text, gemmi.Format.PDB)

# keep only chain A (HA1) + chain B (HA2) for the trimer
model = structure[0]
chains = [c for c in model if c.name in ("A","B")]
out = {"atoms": []}
for chain in chains:
    for residue in chain:
        for atom in residue:
            out["atoms"].append({
                "element": atom.element.name(),
                "x": atom.pos.x,
                "y": atom.pos.y,
                "z": atom.pos.z,
                "b": atom.b_iso,
                "occ": atom.occ,
                "resi": residue.seqid.num,
                "chain": chain.name
            })
json.dump(out, open(f"{pdb_id}.json","w"))
Enter fullscreen mode Exit fullscreen mode

The script runs in ~2 seconds on my laptop, spitting a 12 MB JSON that the Unity side consumes at startup. I chose JSON over binary because the headset’s USB‑C link caps at 5 Gbps and the parser on the Quest runs on a single Cortex‑A76 core — keeping the payload text‑based avoids a custom deserializer and keeps the frame budget predictable.

Inside Unity I built a minimal pipeline:

  1. Load JSON on a background thread (Unity’s Task.Run) → NativeArray<Vector3> for positions, NativeArray<float> for B‑factors.
  2. Upload to a ComputeBuffer (GraphicsBuffer.Target.Structured) so the GPU can instance 1 × 1 × 1 cm spheres for each atom.
  3. Shader reads the B‑factor, maps it to a viridis color ramp, and drives point‑size for a “temperature‑factor” glow.
  4. XR Interaction Toolkit gives me pinch‑to‑scale, grab‑to‑rotate, and a custom “slice plane” that clips the buffer in world space.

The first frame hit 72 fps on the Quest 2 (Snapdragon XR2, 6 GB RAM). After enabling GPU Instancing and culling atoms beyond a 2 m radius, the steady‑state climbed to 90 fps — the headset’s native refresh. I logged the numbers in a Profiler overlay; the GPU time per frame settled at 6.3 ms, CPU at 3.1 ms. Good enough to keep the vestibular system happy.

Standing in my living room, I lifted the headset, and the HA trimer floated at eye level, each monomer a ribbon of teal, amber, and violet. The mutated residues — Q226L, G228S — glowed hot orange because their B‑factors spiked in the deposited model. I could reach into the receptor‑binding site, pinch the side chain of Leu226, and watch the rotamer flip in real time. The compute shader recomputed the clash score on the fly (a simple Lennard‑Jones term) and coloured the atom red when the distance dropped below 2.5 Å. It wasn’t a full docking run, but it gave me an intuition no static image ever could: the pocket widens just enough for a human‑type α2‑6 sialic acid to slip in.

I spent the next three hours iterating:

  • Slice plane at 45° to isolate the HA1 head — revealed a water‑mediated hydrogen bond network that the paper only mentioned in a table.
  • Distance ruler (two controller rays) measured the Cα‑Cα span between the 220‑loop and the 130‑loop: 13.2 Å, matching the cryo‑EM map within 0.3 Å.
  • Annotation layer — I dropped voice‑recorded notes (“possible glycosylation shift at N158”) that Unity stored as world‑anchored TextMeshPro objects. They persist across sessions via PlayerPrefs.

The biggest surprise wasn’t the geometry; it was the feeling of scale. On a monitor, a 150 kDa trimer is a thumbnail. In VR, the same molecule looms like a sculpture you can walk around. My neck ached after an hour — a reminder that ergonomics matter as much as frame‑time. I added a “sit‑down” mode that re‑centers the model at waist height and reduces the interpupillary distance scaling factor by 0.85, cutting the neck torque in half.

When I finally lifted the headset, the coffee was stone cold, but the notebook on my desk was full of concrete hypotheses:

  1. Leu226 creates a hydrophobic pocket that could accommodate the C‑5 acetyl group of human sialic acid — test by mutagenesis.
  2. Gly228Ser adds a side‑chain hydroxyl that may form a water bridge to the 130‑loop — solve a higher‑resolution map.
  3. The 220‑loop flexibility (B‑factor > 80 Ų) suggests an induced‑fit mechanism — run accelerated MD.

I didn’t need a press release to feel the impact. The VR session turned a headline into a set of testable, structural questions I can hand to the wet‑lab collaborators on Monday.

If you’ve ever stared at a PDB file and wished you could step inside the density, give a headset a try. The toolchain is open, the hardware is affordable, and the insight is visceral. You might find the next mutation before it makes the news.

Top comments (0)