OPUS-ET Visualize (in-cell scenes)
Places the refined map at every particle pose inside the tomogram, colored by
conformational state, via ChimeraX + ArtiaX.
Agent Rules — read before acting
- Read before editing; verify command behavior before asserting.
- Reconcile pixel sizes explicitly — never assume star coords and tomogram share
a pixel size.
- Fair coloring. When showing several candidate volumes/states side by side (e.g. a
k-means state gallery), color each distinctly (or all neutrally) — never highlight a
subset (e.g. "the selected ones in blue"), which pre-biases the viewer before the evidence
is in. Use one consistent palette across the gallery and the latent UMAP so a state is the
same color in both. Distinct 20-colors: golden-angle hue spacing gives good neighbour
contrast (
h=(i*0.618)%1, s≈0.68, v≈0.88).
- ChimeraX
tile resets lighting — run lighting soft (and any lighting command)
AFTER tile #* columns N, or it won't apply.
- Judge Gate-3 state resolution in 3D at a HIGH percentile contour, not by CC. Render the
k-means state gallery with
gen_gallery_cxc.py --percentile 98 (per-map 98th-percentile
contour). ChimeraX's default/auto contour sits too low and renders high-res detail as
low-density "speckle" that reads as junk. Two traps invert the ranking: (a) a sharp
high-res map correlates less with the blurry averaged consensus / low-res template, so
compare_to_template.py's CC ranking pushes the BEST states to the BOTTOM — treat low
consensus-CC as a POSSIBLE high-res signal, not junk, and cross-check in 3D; (b) a cleanly
separated, abundant latent-UMAP island is often the best-aligned/sharpest population, not an
artifact. (Real case: ribo z8_expanded k17/18/19 were the high-res ribosomes but ranked
last by CC and looked grainy at auto-contour.)
Inputs
- WARP/RELION star:
rlnCoordinateX/Y/Z, rlnAngleRot/Tilt/Psi, rlnMicrographName.
- Tomogram MRC (cell context) and its pixel size (
--tomo-angpix or MRC header).
- Refined/averaged map MRC.
- Optional per-particle
labels.pkl from opus-et-analysis k-means (color by state).
--coords-angpix: pixel size of the star coordinates.
Convention landmine — DEFUSE FIRST (spec §7.1)
ArtiaX vs RELION angle + pixel-size conventions are the top risk. Before rendering
thousands, validate ONE particle:
particle_transform(row, tomo_angpix) gives [R|t] for particle N.
- Load the map into ChimeraX and place it with that transform; compare against a
manual
fitmap of the map into the tomogram at that particle.
- Only once one particle lands correctly, render the full scene.
euler_to_matrix uses the RELION ZYZ Euler_angles2matrix convention.
ArtiaX 0.7.0 scene recipe (verified locally on the Mac)
emit_cxc was rewritten against real ArtiaX 0.7.0 behavior (5 tests cover the
pure-Python .cxc generation, and run anywhere via the repo .venv). Old
M1-stub commands like artiax open particles and artiax attach ... geomodel
do not exist in 0.7.0 — do not use them.
- Renders run LOCALLY on the Mac, not on the cluster. ChimeraX 1.10 +
ArtiaX 0.7.0 at
/Applications/ChimeraX-1.10.app/Contents/bin/ChimeraX. Pull
the maps/poses (.mrc, sel_*.star) down from the cluster first, then render.
- GUI required. ArtiaX's commands only register once its GUI is up. Run
scenes as
ChimeraX --exit scene.cxc — not --nogui/--offscreen.
- Deterministic model IDs after
artiax start -> #1 (#1.1 Tomograms,
#1.2 Particle Lists, #1.3 Geometric Models):
artiax open tomo <mrc> -> #1.1.1; each plain open <map.mrc> -> #2,
#3, ...; each open <star> format relion -> #1.2.1, #1.2.2, ....
- Particle lists load via plain
open <star> format relion — there is no
artiax open particles in 0.7.0 (generic artiax open errors telling you to
use plain open). Two star readers are registered ('RELION STAR file',
nickname relion; 'RELION5 STAR file', nickname relion5) — you must pass
format relion for old RELION-3.x single-data_-block stars or it errors
"Multiple formats ... support .star suffix".
- Coordinate scale (critical). ArtiaX places
rlnCoordinate* at 1.0 A/px
by default. Rescale with
artiax particles #<pl> originScaleFactor <coords_angpix> (e.g. 4.2) so
particles register with the tomogram. Symptom if omitted: the particle cloud
is ~`coords_angpix`x too small vs the tomogram. The map needs no
scaling — its size comes from its MRC header voxel size.
- Attach + style:
artiax attach #<map> toParticleList #<pl> (map model
must be a Volume); artiax show #<pl> surface; hide #<pl>.3 models (hide
auto markers); color #<pl> <color>.
- Contour: set the map surface to an absolute level (mean + N·sigma)
before attach so the contour is identical across all instances. Crop the
map to a tight box (strip solvent padding) to cut memory when instancing
thousands of copies.
emit_cxc exposes this via contour_level/contour_sd.
- Optional translucent tomogram slab for cell context: open a plain second
copy of the tomogram,
volume #N style image, a faint symmetric transfer
function (cryo-ET density is ~0-mean, e.g.
volume #N level -0.008,0.7 level -0.002,0.1 level 0.002,0.1 level 0.008,0.7),
color light gray, and hide the ArtiaX orthoslice (hide #1.1.1 models).
emit_cxc's tomo_transfer param controls the level string.
emit_cxc params: coords_angpix, contour_level/contour_sd,
tomo_transfer, movie_out.
- Judgment note (cross-ref Gate-3 above): the sharpest/high-res state map
reads as fragmented "speckle" at a tight contour while blurry low-res maps
look deceptively clean — don't let that bias which state you pick.
Usage
M1: scripts/gen_artiax_scene.py is a library, not a CLI (no __main__).
The conductor (or you) imports it and calls its functions directly — there is
no python scripts/gen_artiax_scene.py ... invocation yet.
import pickle
import gen_artiax_scene as gs
labels = pickle.load(open("analyze.39/kmeans12/labels.pkl", "rb"))
df = gs.attach_labels(
gs.reconcile_coords(gs.read_particles("picks.star"),
coords_angpix=A, tomo_angpix=A),
labels,
)
stars = {s: gs.write_relion_star(df, f"state{s}.star", state=s)
for s in sorted(set(df["state"]))}
gs.emit_cxc(tomogram="TS_026.mrc", map_path="ref.mrc",
state_stars=stars, out_cxc="scene.cxc")
Produces one RELION star per state plus scene.cxc.
M2 target — not yet wired. The plan calls for a real main()/argparse CLI
wrapping the same calls (see the M1 plan's "Notes for M2–M5"):
python scripts/gen_artiax_scene.py \
--star picks.star --tomogram TS_026.mrc --map ref.mrc \
--labels analyze.39/kmeans12/labels.pkl \
--coords-angpix <A> --tomo-angpix <A> \
--out-cxc scene.cxc --out-star-prefix state
Open the resulting scene in ChimeraX:
ChimeraX --exit scene.cxc (GUI required — ArtiaX commands don't register
under --nogui/--offscreen; see the ArtiaX 0.7.0 recipe above).
Raw-density marker reveals — show the density, don't replace it
The finale (above) places the refined map at each pose — idealized, and it
hides the raw tomogram. To instead reveal the raw reconstruction with the
picks only marked (validation, or a rare-species "here it really is" shot):
- Per-particle zoomed gallery —
scripts/particle_gallery.py (20 tests).
One small crop per pick, taken at the pick's own Z-plane, marker drawn so the
interior stays visible. Reuses tm_picks_overlay's pick reader + Å coordinate
reconciliation. Best for a sparse species where a whole-slab overlay loses it.python scripts/particle_gallery.py --tomogram TS_028_bin2.mrc \
--picks sel_fas30.star --tomo TS_028 --coords-angpix 4.2 \
--style ring --num 24 --half 17 --species FAS -o fas_raw_gallery.png \
--bild fas_markers.bild # optional: 3D markers for the scan below
- Whole-slab context is already
tm_picks_overlay.py (mean-projected slabs,
ring markers, all/top-N). Use it for "where in the cell", the gallery for "what
the density looks like".
- Marker style is the reveal/spot trade-off (
--style): ring (open circle,
interior untouched — best for revealing density), transparent (tinted disc,
density shows through), solid (opaque — unmissable but hides the particle).
For a moving Z-scan, solid reads best (a thin ring flickers against the grain).
- Scannable Z-scan reveal (plain ChimeraX, not ArtiaX). ArtiaX's orthoslice
can't be driven by command, so scan a plain
volume image-plane with the picks
as fixed BILD markers (--bild above); each barrel appears under its marker as
the plane crosses its Z:open TS_028_bin2.mrc ; open fas_markers.bild
volume #1 style image ; volume #1 color white
volume #1 level -0.016,0 level 0.016,1 ; volume #1 planes z,62
set bgColor black ; camera ortho ; view orient
movie record ; perframe "volume #1 planes z,$1" range 62,176 frames 90 ; wait 90
perframe stop ; movie encode fas_scan.mp4 framerate 24 quality medium
Molecular-sociology / cellular-context render (hero aesthetic) — in emit_cxc
This IS gen_artiax_scene.emit_cxc — placing each species' refined map at every
pose in the tomogram. The hero look (molecules as a 3D cloud spilling past a
floating slab, not a flat decorated micrograph) is opt-in on the same call:
emit_cxc(tomogram="TS_028_bin4.mrc", map_path=None,
state_stars={0: "ribo_TS028.star", 1: "fas_TS028.star"},
state_maps={0: "ribo.mrc", 1: "fas.mrc"},
state_contours={0: 0.010, 1: 0.0072},
state_colors={0: "cornflower blue", 1: "gold"},
coords_angpix=4.2, tilt_x=-55, # tilt so the slab is edge-on
silhouettes=True, # 3D pop; safe at any instance count
still_out="finale_still.png",
movie_out="finale.mp4", movie_rock=30, # rock +-30, NOT a 360 (thin slab)
movie_step=3, out_cxc="finale.cxc")
silhouettes needs the tilt (a face-on view flattens it and silhouettes outline
only near-plane particles). Do NOT pass shadows=True for a thousands-of-
instances scene — the shadow map covers every placed copy and even a single
still hangs (learned the hard way at 3,387 ribosomes). Shadows are fine for the
single-map showcases (M-spins) and small scenes (≲2,000 instances, e.g. TS_029);
the big in-cell finale uses silhouettes + depth-cue only. movie_step coarsens
surfaces for a tractable movie (use 3, not 2, for thousands of instances);
movie_rock avoids the edge-on midpoint of a 360.
Defaults (all off) reproduce the plain turntable. Pick the tomogram by
cross-referencing organelle content (clean *_13.48Apx.png central slices)
against per-tomogram particle counts (grep -oE "TS_0[0-9]+" sel.star | sort | uniq -c, ÷5 for the star's 5 mentions/row). Rotating single-map showcases (the M
result) are a plain-ChimeraX open map ; volume level <mean+4sd> ; surface dust #1 size 120 ; lighting shadows/silhouettes ; turn y 3 120 turntable. See
demo/render_commands.md §B/§B3, demo/finale/build_ts029_cell_scene.py.
ChimeraX/ArtiaX gotchas (learned the hard way)
- Headless
--offscreen/--nogui rendering often fails. ChimeraX offscreen
rendering needs a working OSMesa / virtual-display GL context that many machines
(especially cluster nodes) lack, so the render comes back blank or errors. GUI mode
is the reliable path for all renders, not just ArtiaX — if a headless render
fails, run it in a local GUI session. (ArtiaX is GUI-only regardless; its commands
don't even register without the GUI.)
view does NOT reset orientation — it re-fits zoom but keeps the current
rotation. view; turn x -38 after a prior turn x -55 silently stacks to −93°
(edge-on). Re-issue the full setup, or view orient, between angles.
- Shadows scale badly with ArtiaX instance count.
lighting shadows on
thousands of placed copies makes even a single still hang (the shadow map
covers every instance) — a 3,387-ribosome still never finished. Use silhouettes
only for the big in-cell scene; keep shadows for single-map showcases and
scenes ≲2,000 instances. For a thousands-of-instances movie also use volume … step 3 (not 2) so it stays ~2 min, not ~6.
volume … planes z,N breaks the ArtiaX orthoslice (goes black) — it only
works on a plain (non-ArtiaX) volume. That's why the Z-scan uses plain
ChimeraX + BILD markers rather than ArtiaX.
turn y 360° on a thin slab goes edge-on mid-spin and hides in-plane
features (an organelle arc). For a slab, rock ±~30° instead of a full turntable.
- Silhouettes at a face-on view only outline near-plane particles (the opaque
slice occludes the rest) → an uneven, messy subset. They render cleanly once the
slab is tilted edge-on so molecules float clear of it.
- Python split on the Mac render box: the ChimeraX-bundled python has
matplotlib but NOT mrcfile; the repo
.venv (3.14) has both. Extract crops with
a python that has mrcfile, render figures with either. Verify an mp4 without
ffmpeg via qlmanage -t -s 1100 -o <dir> movie.mp4 (QuickLook thumbnail).
Status
M1: scene generation + single-particle validation. ArtiaX command syntax is
verified against ArtiaX 0.7.0 / ChimeraX 1.10 locally on the Mac (see recipe
above); the cluster is only the source of the .mrc/.star inputs, not the
render/verification host. Added this
session: particle_gallery.py (raw-density marker galleries + BILD markers, 20
tests), the cellular-context render aesthetic, and the scannable Z-scan reveal.
Files in this skill
scripts/
gen_artiax_scene.py # in-cell finale — emit_cxc() places a refined map at every pose
# (library; driven by demo/finale/build_insitu_scene.py)
particle_gallery.py # CLI — per-particle zoomed raw-density gallery + BILD 3D markers (reveal mode)
tm_picks_overlay.py # CLI — pick markers on mean-projected raw slabs (whole-slab context)
slice_preview.py # CLI — central-slice tomogram previews (Gate-1 alignment QC)
tests/ # pytest — test_scene_pose + test_scene_coords (gen_artiax_scene),
# test_particle_gallery, test_tm_picks_overlay, test_slice_preview
No references/ — the ArtiaX recipe + gotchas are inline above.
1---2name: opus-et-visualize3description: Generate in-cell molecular visualizations for cryo-ET results. Two modes — (1) place a refined/averaged map at every particle pose inside its original tomogram in ChimeraX/ArtiaX, colored by OPUS-ET conformational state (the finale look); (2) REVEAL the raw density instead of replacing it — mark picks on the raw tomogram (per-particle zoomed gallery via particle_gallery.py, or slab overlays via tm_picks_overlay.py) with ring/transparent/solid markers, and scan the slice through Z. Use when the user wants molecules in cellular context, a hero in-cell render, or to show/validate that picks land on real raw density.4---56# OPUS-ET Visualize (in-cell scenes)78Places the refined map at every particle pose inside the tomogram, colored by9conformational state, via ChimeraX + ArtiaX.1011## Agent Rules — read before acting12- Read before editing; verify command behavior before asserting.13- Reconcile pixel sizes explicitly — never assume star coords and tomogram share14 a pixel size.15- **Fair coloring.** When showing several candidate volumes/states side by side (e.g. a16 k-means state gallery), color each **distinctly** (or all neutrally) — never highlight a17 subset (e.g. "the selected ones in blue"), which pre-biases the viewer before the evidence18 is in. Use one consistent palette across the gallery and the latent UMAP so a state is the19 same color in both. Distinct 20-colors: golden-angle hue spacing gives good neighbour20 contrast (`h=(i*0.618)%1`, s≈0.68, v≈0.88).21- **ChimeraX `tile` resets lighting** — run `lighting soft` (and any `lighting` command)22 AFTER `tile #* columns N`, or it won't apply.23- **Judge Gate-3 state resolution in 3D at a HIGH percentile contour, not by CC.** Render the24 k-means state gallery with `gen_gallery_cxc.py --percentile 98` (per-map 98th-percentile25 contour). ChimeraX's default/auto contour sits too low and renders high-res detail as26 low-density "speckle" that reads as junk. Two traps invert the ranking: (a) a sharp27 high-res map correlates *less* with the blurry averaged consensus / low-res template, so28 `compare_to_template.py`'s CC ranking pushes the BEST states to the BOTTOM — treat low29 consensus-CC as a POSSIBLE high-res signal, not junk, and cross-check in 3D; (b) a cleanly30 separated, abundant latent-UMAP island is often the best-aligned/sharpest population, not an31 artifact. (Real case: ribo `z8_expanded` k17/18/19 were the high-res ribosomes but ranked32 last by CC and looked grainy at auto-contour.)3334## Inputs35- WARP/RELION star: `rlnCoordinateX/Y/Z`, `rlnAngleRot/Tilt/Psi`, `rlnMicrographName`.36- Tomogram MRC (cell context) and its pixel size (`--tomo-angpix` or MRC header).37- Refined/averaged map MRC.38- Optional per-particle `labels.pkl` from `opus-et-analysis` k-means (color by state).39- `--coords-angpix`: pixel size of the star coordinates.4041## Convention landmine — DEFUSE FIRST (spec §7.1)42ArtiaX vs RELION angle + pixel-size conventions are the top risk. Before rendering43thousands, validate ONE particle:441. `particle_transform(row, tomo_angpix)` gives `[R|t]` for particle N.452. Load the map into ChimeraX and place it with that transform; compare against a46 manual `fitmap` of the map into the tomogram at that particle.473. Only once one particle lands correctly, render the full scene.48`euler_to_matrix` uses the RELION ZYZ `Euler_angles2matrix` convention.4950## ArtiaX 0.7.0 scene recipe (verified locally on the Mac)51`emit_cxc` was rewritten against real ArtiaX 0.7.0 behavior (5 tests cover the52pure-Python `.cxc` generation, and run anywhere via the repo `.venv`). Old53M1-stub commands like `artiax open particles` and `artiax attach ... geomodel`54**do not exist in 0.7.0** — do not use them.5556- **Renders run LOCALLY on the Mac, not on the cluster.** ChimeraX 1.10 +57 ArtiaX 0.7.0 at `/Applications/ChimeraX-1.10.app/Contents/bin/ChimeraX`. Pull58 the maps/poses (`.mrc`, `sel_*.star`) down from the cluster first, then render.59- **GUI required.** ArtiaX's commands only register once its GUI is up. Run60 scenes as `ChimeraX --exit scene.cxc` — **not** `--nogui`/`--offscreen`.61- **Deterministic model IDs** after `artiax start` -> `#1` (`#1.1` Tomograms,62 `#1.2` Particle Lists, `#1.3` Geometric Models):63 `artiax open tomo <mrc>` -> `#1.1.1`; each plain `open <map.mrc>` -> `#2`,64 `#3`, ...; each `open <star> format relion` -> `#1.2.1`, `#1.2.2`, ....65- **Particle lists load via plain `open <star> format relion`** — there is no66 `artiax open particles` in 0.7.0 (generic `artiax open` errors telling you to67 use plain `open`). Two star readers are registered ('RELION STAR file',68 nickname `relion`; 'RELION5 STAR file', nickname `relion5`) — you must pass69 `format relion` for old RELION-3.x single-`data_`-block stars or it errors70 "Multiple formats ... support .star suffix".71- **Coordinate scale (critical).** ArtiaX places `rlnCoordinate*` at 1.0 A/px72 by default. Rescale with73 `artiax particles #<pl> originScaleFactor <coords_angpix>` (e.g. `4.2`) so74 particles register with the tomogram. Symptom if omitted: the particle cloud75 is ~`coords_angpix`x too small vs the tomogram. The **map** needs no76 scaling — its size comes from its MRC header voxel size.77- **Attach + style:** `artiax attach #<map> toParticleList #<pl>` (map model78 must be a Volume); `artiax show #<pl> surface`; `hide #<pl>.3 models` (hide79 auto markers); `color #<pl> <color>`.80- **Contour:** set the map surface to an **absolute** level (mean + N·sigma)81 *before* attach so the contour is identical across all instances. Crop the82 map to a tight box (strip solvent padding) to cut memory when instancing83 thousands of copies. `emit_cxc` exposes this via `contour_level`/`contour_sd`.84- **Optional translucent tomogram slab** for cell context: open a plain second85 copy of the tomogram, `volume #N style image`, a faint *symmetric* transfer86 function (cryo-ET density is ~0-mean, e.g.87 `volume #N level -0.008,0.7 level -0.002,0.1 level 0.002,0.1 level 0.008,0.7`),88 `color light gray`, and hide the ArtiaX orthoslice (`hide #1.1.1 models`).89 `emit_cxc`'s `tomo_transfer` param controls the level string.90- `emit_cxc` params: `coords_angpix`, `contour_level`/`contour_sd`,91 `tomo_transfer`, `movie_out`.92- **Judgment note (cross-ref Gate-3 above):** the sharpest/high-res state map93 reads as fragmented "speckle" at a tight contour while blurry low-res maps94 look deceptively clean — don't let that bias which state you pick.9596## Usage97**M1: `scripts/gen_artiax_scene.py` is a library, not a CLI (no `__main__`).**98The conductor (or you) imports it and calls its functions directly — there is99no `python scripts/gen_artiax_scene.py ...` invocation yet.100101```python102import pickle103import gen_artiax_scene as gs104105labels = pickle.load(open("analyze.39/kmeans12/labels.pkl", "rb"))106df = gs.attach_labels(107 gs.reconcile_coords(gs.read_particles("picks.star"),108 coords_angpix=A, tomo_angpix=A),109 labels,110)111stars = {s: gs.write_relion_star(df, f"state{s}.star", state=s)112 for s in sorted(set(df["state"]))}113gs.emit_cxc(tomogram="TS_026.mrc", map_path="ref.mrc",114 state_stars=stars, out_cxc="scene.cxc")115```116Produces one RELION star per state plus `scene.cxc`.117118**M2 target — not yet wired.** The plan calls for a real `main()`/argparse CLI119wrapping the same calls (see the M1 plan's "Notes for M2–M5"):120```bash121python scripts/gen_artiax_scene.py \122 --star picks.star --tomogram TS_026.mrc --map ref.mrc \123 --labels analyze.39/kmeans12/labels.pkl \124 --coords-angpix <A> --tomo-angpix <A> \125 --out-cxc scene.cxc --out-star-prefix state126```127128Open the resulting scene in ChimeraX:129`ChimeraX --exit scene.cxc` (GUI required — ArtiaX commands don't register130under `--nogui`/`--offscreen`; see the ArtiaX 0.7.0 recipe above).131132## Raw-density marker reveals — show the density, don't replace it133The finale (above) places the *refined* map at each pose — idealized, and it134hides the raw tomogram. To instead **reveal the raw reconstruction** with the135picks only *marked* (validation, or a rare-species "here it really is" shot):136137- **Per-particle zoomed gallery — `scripts/particle_gallery.py`** (20 tests).138 One small crop per pick, taken at the pick's own Z-plane, marker drawn so the139 interior stays visible. Reuses `tm_picks_overlay`'s pick reader + Å coordinate140 reconciliation. Best for a sparse species where a whole-slab overlay loses it.141 ```bash142 python scripts/particle_gallery.py --tomogram TS_028_bin2.mrc \143 --picks sel_fas30.star --tomo TS_028 --coords-angpix 4.2 \144 --style ring --num 24 --half 17 --species FAS -o fas_raw_gallery.png \145 --bild fas_markers.bild # optional: 3D markers for the scan below146 ```147- **Whole-slab context** is already `tm_picks_overlay.py` (mean-projected slabs,148 ring markers, all/top-N). Use it for "where in the cell", the gallery for "what149 the density looks like".150- **Marker style is the reveal/spot trade-off** (`--style`): `ring` (open circle,151 interior untouched — best for revealing density), `transparent` (tinted disc,152 density shows through), `solid` (opaque — unmissable but hides the particle).153 For a moving Z-scan, `solid` reads best (a thin ring flickers against the grain).154- **Scannable Z-scan reveal (plain ChimeraX, not ArtiaX).** ArtiaX's orthoslice155 can't be driven by command, so scan a plain `volume` image-plane with the picks156 as fixed BILD markers (`--bild` above); each barrel appears under its marker as157 the plane crosses its Z:158 ```159 open TS_028_bin2.mrc ; open fas_markers.bild160 volume #1 style image ; volume #1 color white161 volume #1 level -0.016,0 level 0.016,1 ; volume #1 planes z,62162 set bgColor black ; camera ortho ; view orient163 movie record ; perframe "volume #1 planes z,$1" range 62,176 frames 90 ; wait 90164 perframe stop ; movie encode fas_scan.mp4 framerate 24 quality medium165 ```166167## Molecular-sociology / cellular-context render (hero aesthetic) — in `emit_cxc`168This IS `gen_artiax_scene.emit_cxc` — placing each species' refined map at every169pose in the tomogram. The **hero look** (molecules as a 3D cloud spilling past a170floating slab, not a flat decorated micrograph) is opt-in on the same call:171```python172emit_cxc(tomogram="TS_028_bin4.mrc", map_path=None,173 state_stars={0: "ribo_TS028.star", 1: "fas_TS028.star"},174 state_maps={0: "ribo.mrc", 1: "fas.mrc"},175 state_contours={0: 0.010, 1: 0.0072},176 state_colors={0: "cornflower blue", 1: "gold"},177 coords_angpix=4.2, tilt_x=-55, # tilt so the slab is edge-on178 silhouettes=True, # 3D pop; safe at any instance count179 still_out="finale_still.png",180 movie_out="finale.mp4", movie_rock=30, # rock +-30, NOT a 360 (thin slab)181 movie_step=3, out_cxc="finale.cxc")182```183`silhouettes` needs the tilt (a face-on view flattens it and silhouettes outline184only near-plane particles). **Do NOT pass `shadows=True` for a thousands-of-185instances scene** — the shadow map covers every placed copy and even a single186still hangs (learned the hard way at 3,387 ribosomes). Shadows are fine for the187single-map showcases (M-spins) and small scenes (≲2,000 instances, e.g. TS_029);188the big in-cell finale uses silhouettes + depth-cue only. `movie_step` coarsens189surfaces for a tractable movie (use 3, not 2, for thousands of instances);190`movie_rock` avoids the edge-on midpoint of a 360.191Defaults (all off) reproduce the plain turntable. Pick the tomogram by192cross-referencing organelle content (clean `*_13.48Apx.png` central slices)193against per-tomogram particle counts (`grep -oE "TS_0[0-9]+" sel.star | sort |194uniq -c`, ÷5 for the star's 5 mentions/row). Rotating single-map showcases (the M195result) are a plain-ChimeraX `open map ; volume level <mean+4sd> ; surface dust196#1 size 120 ; lighting shadows/silhouettes ; turn y 3 120` turntable. See197`demo/render_commands.md` §B/§B3, `demo/finale/build_ts029_cell_scene.py`.198199## ChimeraX/ArtiaX gotchas (learned the hard way)200- **Headless `--offscreen`/`--nogui` rendering often fails.** ChimeraX offscreen201 rendering needs a working OSMesa / virtual-display GL context that many machines202 (especially cluster nodes) lack, so the render comes back blank or errors. GUI mode203 is the reliable path for **all** renders, not just ArtiaX — if a headless render204 fails, run it in a local GUI session. (ArtiaX is GUI-only regardless; its commands205 don't even register without the GUI.)206- **`view` does NOT reset orientation** — it re-fits zoom but keeps the current207 rotation. `view; turn x -38` after a prior `turn x -55` silently stacks to −93°208 (edge-on). Re-issue the full setup, or `view orient`, between angles.209- **Shadows scale badly with ArtiaX instance count.** `lighting shadows` on210 thousands of placed copies makes even a single still hang (the shadow map211 covers every instance) — a 3,387-ribosome still never finished. Use silhouettes212 only for the big in-cell scene; keep shadows for single-map showcases and213 scenes ≲2,000 instances. For a thousands-of-instances movie also use `volume …214 step 3` (not 2) so it stays ~2 min, not ~6.215- **`volume … planes z,N` breaks the ArtiaX orthoslice** (goes black) — it only216 works on a *plain* (non-ArtiaX) volume. That's why the Z-scan uses plain217 ChimeraX + BILD markers rather than ArtiaX.218- **`turn y` 360° on a thin slab goes edge-on mid-spin** and hides in-plane219 features (an organelle arc). For a slab, rock ±~30° instead of a full turntable.220- **Silhouettes at a face-on view only outline near-plane particles** (the opaque221 slice occludes the rest) → an uneven, messy subset. They render cleanly once the222 slab is tilted edge-on so molecules float clear of it.223- **Python split on the Mac render box:** the ChimeraX-bundled python has224 matplotlib but NOT mrcfile; the repo `.venv` (3.14) has both. Extract crops with225 a python that has mrcfile, render figures with either. Verify an mp4 without226 ffmpeg via `qlmanage -t -s 1100 -o <dir> movie.mp4` (QuickLook thumbnail).227228## Status229M1: scene generation + single-particle validation. ArtiaX command syntax is230verified against ArtiaX 0.7.0 / ChimeraX 1.10 locally on the Mac (see recipe231above); the cluster is only the source of the .mrc/.star inputs, not the232render/verification host. Added this233session: `particle_gallery.py` (raw-density marker galleries + BILD markers, 20234tests), the cellular-context render aesthetic, and the scannable Z-scan reveal.235236## Files in this skill237```238scripts/239 gen_artiax_scene.py # in-cell finale — emit_cxc() places a refined map at every pose240 # (library; driven by demo/finale/build_insitu_scene.py)241 particle_gallery.py # CLI — per-particle zoomed raw-density gallery + BILD 3D markers (reveal mode)242 tm_picks_overlay.py # CLI — pick markers on mean-projected raw slabs (whole-slab context)243 slice_preview.py # CLI — central-slice tomogram previews (Gate-1 alignment QC)244tests/ # pytest — test_scene_pose + test_scene_coords (gen_artiax_scene),245 # test_particle_gallery, test_tm_picks_overlay, test_slice_preview246```247No `references/` — the ArtiaX recipe + gotchas are inline above.