Skip to main content

Mol* illustrative parity

Reference for megane's illustrative representation and color mode, and where they do and do not match Mol*. Every value below was read out of molstar 5.11.0 (npm view molstar), not from memory; the palette and the lightened carbon colors were produced by running Mol*'s own mol-util/color code.

What "the Mol* illustrative look" actually is

It is not one setting. Mol*'s Illustrative appearance is three things applied together, and the recognisable Goodsell look needs all three:

PieceWhere it lives in Mol*megane
spacefill at full vdW radiusillustrative representation presetrepresentation: "illustrative"
ignoreLight (flat, unlit fill)same preset✅ same mode
illustrative color themesame presetcolor: "illustrative"
occlusion (SSAO)Illustrative quick stylecanvas3d.postprocessing⚠️ approximated in-shader
outline (screen-space, depth-based)same quick style⚠️ approximated in-shader

In the Mol* UI these are two separate buttons: Apply Representation → Spacefill applies the illustrative preset, and Apply Style → Illustrative turns on the postprocessing. Note the Spacefill button applies the preset named illustrative, not a plain spacefill preset.

The color theme (megane matches this exactly)

mol-theme/color/illustrative.ts reduces to two lines:

const baseColor = styleColor(location, false);
return typeSymbol === "C" ? Color.lighten(baseColor, props.carbonLightness) : baseColor;

Every atom takes the entity color. Carbon is only a lightened shade of that same color. Non-carbon atoms do not get CPK element colors — that is Mol*'s separate element-symbol theme. This is what makes each chain read as one flat body rather than as red/blue confetti.

ParameterMol* value
default styleentity-id (the preset passes { overrideWater: true })
carbonLightness0.8, range −6…6
palettemany-distinct, indexed serial % 25megane deviates, see below
unresolved entity0xFAFAFA
water (overrideWater)0xFF0D0D
theme fallback (non-atomic location)0xEEEEEE

Color.lighten

Color.lighten(c, amount) is Lab.darken(c, -amount), which is:

L* += Kn * amount     (Kn = 18)     a*, b* unchanged

So carbonLightness: 0.8 means L* + 14.4 in CIE Lab (D65 white point, sRGB companding, chroma.js-derived breakpoints). It preserves hue and saturation, unlike a blend toward white, which washes the color out. megane ports this in src/colorSchemes.ts (lightenLab) including the 8-bit rounding, so its output is bit-identical — tests/ts/colorSchemes.test.ts asserts all 25 palette entries against values generated by Mol* itself.

The palette — a deliberate deviation

Mol*'s default is its many-distinct list (Dark2 + Set1 + Set2, 25 entries, cycled i % 25):

dark-2  1b9e77 d95f02 7570b3 e7298a 66a61e e6ab02 a6761d 666666
set-1 e41a1c 377eb8 4daf4a 984ea3 ff7f00 ffff33 a65628 f781bf 999999
set-2 66c2a5 fc8d62 8da0cb e78ac3 a6d854 ffd92f e5c494 b3b3b3

megane does not use it. It softens its own CHAIN_COLORS instead, so the mode stays recognisably megane and keeps chain identity consistent with the byChain color mode. The transform runs in CIE LCh, which preserves hue exactly:

L* → L* + (92 - L*) * 0.55      C* → C* * 0.5

Mol*'s water color is put through the same transform: raw #ff0d0d is fully saturated, and against a pastel palette it reads as a wall of red that swallows the solute in a solvated system.

Everything else about the theme — the rule, the lightening math, the serial indexing, the fallback color — follows Mol* exactly. lightenLab is asserted bit-for-bit against values generated by Mol*'s own code in tests/ts/colorSchemes.test.ts.

The postprocessing (megane approximates this)

The Illustrative quick style sets, verbatim:

outline:   { scale: 1, color: 0x000000, threshold: 0.33, includeTransparent: true }
occlusion: { multiScale: off, radius: 5, bias: 0.8, blurKernelSize: 15,
blurDepthBias: 0.5, samples: 32, resolutionScale: 1,
color: 0x000000, transparentThreshold: 0.4 }
shadow: off

Mol*'s outline (mol-gl/shader/outlines.frag.ts) walks the 3×3 screen neighbourhood of each pixel and marks it as outline when

abs(selfViewZ - sampleViewZ) > pixelSize * threshold  &&  selfDepth > sampleDepth

Two consequences megane does not currently reproduce:

  • the line is drawn on the farther pixel, so it hugs the outside of the nearer object rather than sitting on the object's own edge;
  • a depth step below the threshold draws nothing, so where two spheres interpenetrate at similar depth there is no line. Outer silhouettes are strong and internal seams are weak — the look has a hierarchy.

megane has no post-processing stack (renderer.render(scene, camera) goes straight to the canvas), so both effects are approximated inside the impostor's own fragment shader:

  • outline — a constant-width band at each sphere's own silhouette, sized from fwidth(). Uniform across every sphere, so it has no hierarchy and it draws every sphere-sphere intersection curve.
  • occlusion — a radial ramp toward that same silhouette. In a spacefill field the dominant occlusion term is contact with neighbouring spheres, which always sits near a sphere's rim, so this recovers most of the sculpted look. It cannot darken a large pocket the way a real SSAO pass does.

Both need no extra render target, so the mode behaves identically in the offscreen render/capture path and on every host. Replacing them with a real depth prepass + SSAO + outline pass is tracked separately; the impostors write gl_FragDepth, so such a pass needs its own depth/normal variants of the atom and bond shaders — a stock three.js SSAOPass would render the flat billboards and produce garbage.

Other known deviations

  • Entities. megane parses no label_entity_id, so chain ID stands in for it. For most PDB entries the two coincide.
  • Water detection. Mol* keys overrideWater off the entity's water type; megane matches residue names (HOH, WAT, SOL, TIP*, …) from atom labels, and skips the override when a structure carries no labels.
  • vdW radii. megane's VDW_RADII differs from Mol*'s ElementVdwRadii for 75 of the 92 elements both define — notably H (1.20 vs 1.10) and most metals. The organic set (C, N, O, S, P, Si, halogens) agrees. megane's table is shared with ball-and-stick sizing and distance-based bond inference, so aligning it is a much wider change than this mode.
  • Single-entity structures. With one chain (most non-PDB formats) the theme is near-monochrome by construction — that is what Mol* does too. Use the byElement color mode for CPK coloring of materials.