https://github.com/mx-e/quick-mol-viewer
Static molecule viewer — render molecules, trajectories, and collections from URL-encoded data. No backend, no build step.
https://github.com/mx-e/quick-mol-viewer
Last synced: about 2 months ago
JSON representation
Static molecule viewer — render molecules, trajectories, and collections from URL-encoded data. No backend, no build step.
- Host: GitHub
- URL: https://github.com/mx-e/quick-mol-viewer
- Owner: mx-e
- Created: 2026-04-21T10:59:16.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-04-21T11:11:40.000Z (2 months ago)
- Last Synced: 2026-04-21T13:12:24.233Z (2 months ago)
- Language: HTML
- Size: 21.5 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# quick-mol-viewer
A single-page, static molecule viewer. All state lives in the URL — no backend, no uploads, no build step. Paste a link, render a molecule.
**Live:** https://mx-e.github.io/quick-mol-viewer/
## Try it
Click any of these (they render in the hosted viewer):
- **Water** (static snapshot, 86 chars): [Link](https://mx-e.github.io/quick-mol-viewer/#d=cgEAAwABAMHKQT8IAQEAAAAAAAD_fxVjAAABgBVjAAA)
- **Benzene rotation** (60-frame trajectory, delta+gzip): [Link](https://mx-e.github.io/quick-mol-viewer/#d=Zx-LCAAAAAAAAv9llk9IVGsYxr-Z809nzkipRLiQiFsraxVlN7l2kdI0SQqhRXdxiQqquUSim7QraRvD8DIVVESLWgRSGGaNhYRgadEqXSVEtJAI_8SccTxnzpyZ-zxn-7r4kHO-ec73_vme9xeJ2KpBdbfWHjTDv0j4t9as8Pf7bzV_KPV-kevmNJ-8X1ya5nOu8__ySd3Bt_8otfCW665-Pll4--d_fM71fum6pdQx77Km1IT3zVfqjLriKvXcvFlQqs3cpivVXvoroVRltsZUajY7hrfj6lFGqdX4kqfU_ngHFOaLl7AG7psolPMN0NkVGcFOzToU4IvGDHR-BPug0-98xv9314c3lNoa_QCdfns39p-LdZUplSgehs6U60eUepF_klfKibyGTpNlFJU6apw0lBoqLNuI1-nDmW_kBnJK9WjVjlJr9gDO3F3eV44zBxZ0Uu5e6LT526EzHvWgk7Q-QmdC_wqdPf5V6LzL1OLMdzZS60p90q9B50DiO858viwZU-p24SWi7nWTyNuqP4p4L2itWAetVAk50TvxdsVbjiv1MDOJPWl3OqtUlVmN9e9EM_a0WI14u-R3hDqP8eRBoQUKNfpYqHMKvzqtpfH2lVsf6hzHnl9eBRSOWHOhzjPs2WRm8LbJ_4KTj7iLiKIzyCGiKX0nortk7UCko9EYor61cRYnn8msIIp6vxcRXSy7h-gaElWIdE4fRNRP8yegM-lugc6W4iR0ThhPodNs_YTOz0gzdIZyPchkwhlGVocLCej0lA8h247dhcx3aQ50duanUM2c24lvLRZZ8S9GE6oZs0bxrR0RdsW99YuobK9Tj-crwQwqfjZ2C9UftOfw26poA7pizKvB_y3eA3zrcakXOh3mEvanzdP41inkCSfPHsFakf2FvB0vPYROffwVdmbim5C9Z8iTUq3eBewf9Vbx26SizkvzNjQ7zUrop0rUqc5WYf90No23k4o6y_EVaDbGW_C2uUQdzx3H-bfn26CzN5LCTstqR4xfjQnofCwmsfOa84ma63cQS230HXSu2nv43dh5xPs9OACd166DfD7Jv0Bu_cgUdA5bCeT8pHEU-TeKTTyP04MzD-RuILd92mbkedkewpn7yruR_4HCms2K74JOg18HnTfRwGXF56Ezoy9A51CgQedDZivOPLxxF7X-rPdDZ1_iB87cVXYO_bDb74fOFfcMeumbP4F4L2vHsF637qPftultyMnNwnOsjzLj2DPmzqL3asxKrH8l2rGnw9rPTvZW49Thr24WqLBNnwh1qHxZo8I3vy3U4a-WPCp0WLOhDpVrTCqMuVQbcRnFoYARzeh1YScz0jdRRtTgM7oPGUaB8zuMhdHtSzBSxGgzXkb32mVWjSIzfNJgtg9bzLwfYYaf5JntaodZHSgww33lzPayzcz3aczwQI7Z9lxW-WORFf9qsPqWxU7YG2HFt-dZ_WsOq_w9YMWTMVb_qs1OqI2y4ql1Vr_VY9elwk7uNNmNL012ZjLs5FGP3VidZdc1h53cGGc3LsfZmZNhJ09n2Y1jHm_BqbCT0yZvR4fJm_I47OQWj7djLstb8Czs5Eyct6M-zptyPOzkiixvx848b-WO0JNjFm_rF4M3dzH07ZzL23pvnbeyKsobOmjztp6N8eauBLyhvQ5v69M8XeJnhI7RbNE9Thh0ki1FOsakS_cYytElujQ6hmPTPXrK6STDBTpGwqF7NPl0rdFoLuxkutmUTmfrDOhgIy7d7NYGXWtOp4M1JOhmF8vobPU-HWwmQzdb8umip7WW0EvprjU6nfZBIR36Ld31lUsX3WRWhF5Kdz1i0Wl_eZnQb-mutwt09Up9NNSh21_Q6PyrfmeoQ7df8ejqLdZ0qEO3rzLp_Gm3MdSh27cHnDITOidO0uL0GY9yErX5nDgpl9Nnj88pc76ME-dAgtPnk85JdGeDE-ddhtMnUeTUO2pwAjZZnIZOhJPxRZ4TcMrlNBwqcOp1l3MCrtmchj0aJ-ONHCfgZofTcD705AWDE1mzOJ13hZ5cl-dEDlxO5x8Bp_C5GCdyv83pvDXKSX13nRO53-F0vl8iFbSZJITnJmnhjCI5THgkhGMeaaG9RCrYHychrMZJC-OK5DCbJSFUZkkLki4kgUi6kAQi6UISiKQLSSCSLiSBSLqQBCLpQhKIpAtJIJIuJIFIupAEIulCEoikC0kgki4kgUi6kAQi6UISiKQLSSCSLiSBSLqQBCLpQhKIpAtJIJIuJIFIupAEIulCEoikC0kgki4kgUi6kAQi6UISiKQLSSCSLiSBSLqQBCLpQhKIpAtJIJIuJIFIupAEIulCEoikC0kgki4kgUi6kAQi6UISiKQLSSCSLiSBSLqQBCLpQhKIpAtJIJIuJIFIupAEIulCEoikC0kgki4kgUi6kAQi6UISiKQLSSCSLiSBSLqQBCLpQhKIpAtJIJIuJIFIupAE8j9Er2R69hAAAA)
- **Collection** (6 heterogeneous mols — water, ammonia, methane, CO₂, methanol, benzene): [Link](https://mx-e.github.io/quick-mol-viewer/#d=Zx-LCAAAAAAAAv9jZGJgYGOoFpFzYGbgYGRkAAMr9Tg5BoZjN0AkCwM7I0x8mwEDw7N3S5__1ASR7NeevWNlYGOESy9TAMGo-1H3QSQMR91nZmDj4IAokbUBkY8Pg0g2oDBCs5QXiLzy5qWkvB6IfHjxyhvV0wwMyQmL9RgYeBjYwIARDDg8QGrdVPTtGBh23QaRP7aDRHbdvngQJA4i_9eDRJwcnuYxMOzbDyIZG0Ai-_ZLTwSJg0gAlDGs0v0AAAA)
In the collection, press `G` (or click ▦) to toggle the 3×3 grid view.
## What it does
Three content modes, all served from one HTML file:
- **Single snapshot** — one molecule, one frame.
- **Trajectory** — one molecule, many frames. Play/pause, scrub, variable speed (0.25×–4×, base 10 fps).
- **Collection** — multiple independent molecules (different atom counts OK). Navigate one-at-a-time or view a 3×3 grid.
## Usage
Two ways to load a molecule:
1. **URL fragment** — `https://mx-e.github.io/quick-mol-viewer/#d=`
2. **Paste field** — click `paste` (top-right) or press `P`, paste either the full URL, the `#d=…` fragment, or just the base64url payload.
Copy-paste artifacts (line wraps, shell `\`-continuations, smart quotes, `%XX` escapes, zero-width chars) are stripped automatically before decoding.
## Encoding
The reference Python encoder is in this repo (`mol_url.py`, requires `numpy`):
```python
from mol_url import encode, encode_collection
# Snapshot: positions shape (N, 3)
url = encode(positions, atomic_numbers)
# Trajectory: positions shape (F, N, 3). Delta-encoded by default.
url = encode(trajectory, atomic_numbers)
# Collection: iterable of (positions, atomic_numbers) pairs.
url = encode_collection([(pos1, Zs1), (pos2, Zs2), ...])
# Get just the opaque payload code (for the paste field), no URL:
code = encode(positions, atomic_numbers, base_url="")
```
Run the module directly to self-test and print example URLs:
```bash
python mol_url.py
```
## Wire format
Base64url-decode the fragment value to get a byte array. First byte is a prefix: `0x67` (`g`) → rest is gzip-compressed; `0x72` (`r`) → rest is raw. After optional ungzip, read a 10-byte little-endian header:
| offset | type | name | notes |
|--------|---------|---------|-------|
| 0 | uint8 | version | `1` |
| 1 | uint8 | flags | bit 0 = delta-encoded frames; bit 1 = collection mode |
| 2 | uint16 | N | global atom count (0 in collection mode) |
| 4 | uint16 | F | frame count, or molecule count if collection mode |
| 6 | float32 | scale | Ångström scaling factor |
Ångström values are recovered as `xyz_A = int16_value * (scale / 32767)`.
**Snapshot / trajectory body** (after the header):
- `uint8 × N` atomic numbers (H=1, C=6, N=7, O=8, F=9, Si=14, P=15, S=16, Cl=17, Br=35, I=53)
- `int16 × F × N × 3` coordinates, frame-major `[f][a][xyz]`
For delta-encoded trajectories (flag bit 0), frame 0 is absolute int16 and subsequent frames are int16 deltas from the previous frame. Decoders must cumulative-sum in int32 (to avoid int16 wrap) before applying the scale.
**Collection body** (flag bit 1 set; header `N` is 0, `F` is the number of molecules). Starting at offset 10, read `F` blocks back-to-back:
- `uint16` N_i (atoms in this molecule)
- `uint8 × N_i` atomic numbers
- `int16 × N_i × 3` coordinates
The `flags & 1` delta bit is not valid in collection mode.
The reference JS decoder (`decodeMolPayload` in `index.html`) handles all of this in ~60 lines.
## Keyboard
| key | action |
|-----------------|--------|
| `Space` | play/pause trajectory |
| `←` / `→` | step frame (trajectory) / molecule (collection, single) / page of 9 (collection, grid) |
| `G` | toggle grid view (collection) |
| `P` | open paste dialog |
| `Esc` | close paste dialog |
| `⌘/Ctrl+Enter` | submit paste |
## Credits
Rendering by [3Dmol.js](https://3dmol.csb.pitt.edu/).