A structural atlas for chemistry students. Type a compound and Ozone resolves it against PubChem, then shows it two ways at once: an interactive 3D conformer and a 2D structural formula with perceived rings, functional groups and a condensed registry profile alongside.
The interface stays deliberately quiet — Lexend, opaque greyscale surfaces, neutral borders — because in v3 the only colour on screen belongs to the chemistry. All 118 elements carry a perceptually distinct hue, so a heteroatom, a halide and a metal are readable at a glance without consulting a legend.
- A 2D structural formula view. The registry's own Kekulé drawing, redrawn as SVG. Ring cycles are perceived from the graph, outlined and badged with their size.
- Structural analysis. A ring inventory, a functional-group census and an index of unsaturation that is cross-checked against the perceived rings and π bonds.
- 118 distinct element colours. A categorical palette, not a CPK copy — see below.
- Multi-source overviews. The intuitive summary is the Wikipedia lead paragraph, with the registry's systematic wording one click behind it.
- Element deep links into the companion site at
ogneson.vercel.app. - Honest provenance and failures. Synthesized geometry is labelled as such, and a missing record explains why instead of just going blank — with an opt-in schematic scaffold for names the registry simply does not carry.
- Mobile-first layout from a phone up.
The standard CPK scheme is unfit for study: carbon and iron are near-black and vanish on a dark ground, while a dozen transition metals share the same brown — two atoms in one molecule can be impossible to tell apart. v3 replaces it with a categorical palette built in two steps:
- Seed the elements a learner meets constantly with their textbook colours — H white, C slate, N blue, O red, S yellow, P orange, halogens green through violet, plus notable metals (Cu, Fe, Ag, Au, Pt, Zn…). A seed is only discarded if it genuinely crowds a neighbour.
- Pack the remaining 84 greedily: each takes the perceptually farthest colour from all the others, biased towards its chemical family so related elements stay related.
Separation is measured in CIELAB and the palette exports minSeparation() so the claim
can be verified rather than trusted. Current figures: all 118 elements, worst pair
ΔE ≈ 10, 98.5% of pairs above 16, median 71, built in ~38 ms.
Colour is used as chemistry, never as decoration. Chrome, panels, bonds and the 3D starfield remain greyscale; bloom is deliberately weak so it cannot bleach the atoms, and bond struts use a darkened relative of their atom's colour so they do not out-shout the atoms they connect.
- Dual viewport — Ball & Stick for connectivity, Space Fill for van der Waals volume, with damped orbit controls and auto-rotation that actually toggles.
- 2D structural formula — hydrogens fold into their parent atom, so display mode
reproduces a printed structural formula (
H3C,CH3,NH2,OH) while skeletal mode leaves bare carbon vertices. Pan, zoom, ring toggles and PNG export. - Ring perception — the shortest path around each bond yields the smallest cycle through it; rings past seven atoms are discarded, so a cage such as C₆₀ reports its pentagons and hexagons without claiming a complete SSSR.
- Structural analysis — ring inventory, functional-group census (amide, ester, carboxylate, carboxylic acid, nitro group, aromatic nitrogen, sulfate, sulfonic acid, thiol, nitrile, acyl halide…), σ/double/triple counts and an index of unsaturation cross-checked against the perceived rings and π bonds.
- Charge-aware — formal charges are parsed from the registry (PubChem files them
either as a plain array or as
aid/valuepairs), drawn as superscripts on the 2D label, and named in the analysis, so an acetate reads as a carboxylate rather than an acid and a salt reports both cation and anion. - Multi-source overview — the plain-language summary is the Wikipedia lead, pulled live. The registry's wording (ChEBI, NLM…) is a click away. Toxicology dossiers are never used as an overview; hazards have their own section. Every source links out.
- Encyclopedia matching — the typed name is tried first, then registry synonyms,
then a search. Disambiguation stubs and "List of…" index articles are rejected, which
is what turns
C60into Fullerene andPenicillin Ginto Benzylpenicillin. - Element deep links — every element in the legend and composition bar opens its page on the companion site in a new tab, so the supplement never loses your structure. The hover tooltip still carries the local study note.
- Correct formula notation — Hill notation for organics, cation-first for
inorganics:
NaCl,Na2SO4,CaCO3,H2SO4,HNO3,C8H10N4O2. - Responsive from a phone up — the header collapses to two rows, the view switcher and caption share one wrapping bar so they cannot overlap, the registry panel becomes a drawer, the legend collapses to a single row, and the renderer drops to a 1.5× pixel ratio on narrow viewports.
- Resilient fetching — throttled requests are retried with backoff, the 2D and 3D downloads are sequenced rather than fired as a burst, and a name resolving to several CIDs is walked until one actually renders.
- Honest failures — an unknown name, an absent conformer, throttling or a dead network each explain themselves and offer compounds to try.
- Accessible contrast — the greyscale interface ramp clears 4.5:1 even at its quietest, and the whole interface audits at full marks.
- Engine: Three.js / WebGL
- Core: SolidJS — signals and resources
- Interface: SolidJS, TailwindCSS, Oxide, Ozone Engine
- Data: PubChem PUG REST & View API, Wikipedia action API, Ozone Engine
- Inorganic compounds are mostly there — the naming is the problem. The
registry files salts, oxides and complexes in full, but publishes a 2D drawing
and no 3D conformer for them, so
Fe2O3arrives as a labelled pseudo-3D lift of a real drawing rather than a measured structure. It also indexes formulas separately from names, soFe2O3is a record the name index will not answer to at all; when a name search comes up empty the formula index is tried as well, and the caption says when that is what happened. - The formula search is strict in two ways worth knowing: it rejects parentheses
(
Ca(OH)2fails, the flattenedCaH2O2works) and it is case-sensitive (FE2O3is a server error whereFe2O3resolves). Both are handled here by flattening to Hill order and restoring conventional casing. - Formula search is asked with a single attempt. It answers non-formulas with a 500 rather than a 404, and retrying that four times would spend the rate-limit budget on input that was never going to match.
- Proteins, MOFs and other very large structures can exceed PubChem's single-record payload limits. Synthesized conformers approximate connectivity, not force-field geometry, and are labelled as synthesized wherever they appear.
- When a name resolves to no record at all, the interface offers a schematic scaffold: atoms placed from the element or the formula, with no bonds drawn, because connectivity is exactly what is missing. It is behind an explicit confirmation that states plainly what the model is not, and every surface that shows it says so again.
- Synthesis is only offered when the query genuinely is chemistry — an element
(
Fe,Iron,Og) or a formula made of real symbols (Fe2O3,Ca(OH)2,SO4^2-). A typo or a stray word is refused rather than rendered as invented carbon atoms, and the page says why instead of offering the button. - A rate-limited or unreachable registry is never reported as "not found". Those states get their own heading and a retry, because telling a learner their compound does not exist because the registry was busy is worse than useless.
- On throttling, the backoff is capped at 2 s and cleared the moment any request succeeds — and also when a retry sequence gives up, since the cooldown is shared. Without that, consecutive throttles compound: the queue was carrying a 6.4 s penalty past the end of a failed sequence, so the next search began late for a throttle it had nothing to do with (measured 5.6 s → 12.0 s → worse; now flat at ~3.7 s, with the following request unaffected).
- "Try again" re-runs the lookup by bumping a counter on the resource source, not
by clearing and restoring the query. That would need a frame boundary between
the two writes, and
requestAnimationFramedoes not fire in a background tab, so the button would do nothing for exactly the learner who switched away mid-search. - Summaries are condensed from their sources and should be verified against the original article.
Developed by @codersilicon