Skip to content

xpict

Molecule depiction for publication-quality SVG — one Rust paint core, with RDKit layout at each language edge (Python, JavaScript/WASM, native Rust).

Gallery · Install · API

Two first-class APIs share the same paint:

API Shape Good for
Single molecule mol → render → toSvg One structure, options, align_to
Declarative document nested JSON (type: "mol" / group) Batches and a document model that is still expanding

Single molecule

Build a mol, render with options, turn the scene into SVG.

import { xpict } from "@xenosite/xpict";

const benzene = xpict.mol("c1ccccc1");
const rendered = await xpict.render(benzene, {
  color: "#0b6e4f",
  atom_shade: [0, 0, 0.2, 0, 0, 0.9],
});
const svg = xpict.toSvg(rendered.scene);

// Align a second mol onto the first pose
const aligned = await xpict.render(xpict.mol("Cc1ccccc1"), {
  align_to: benzene, // or align_to: rendered
});
from xpict import mol, render, to_svg

benzene = mol("c1ccccc1")
rendered = render(benzene, {
    "color": "#0b6e4f",
    "atom_shade": [0, 0, 0.2, 0, 0, 0.9],
})
svg = to_svg(rendered.scene)

aligned = render(mol("Cc1ccccc1"), {"align_to": benzene})
use xpict::{mol, MolRenderOptions};

let mut benzene = mol("c1ccccc1")?;
let rendered = benzene.render(MolRenderOptions {
    color: Some("#0b6e4f".into()),
    atom_shade: Some(vec![0.0, 0.0, 0.2, 0.0, 0.0, 0.9]),
    ..Default::default()
})?;
let svg = rendered.to_svg();
Benzene in green
`color: "#0b6e4f"`
Phenol with atom shading
Atom shading
Toluene
Molecule 2 (`align_to`)

Star / Markush labels on this path use star_labels (chem markup OK) or a CXSMILES trailer when star_labels is omitted:

// Chem markup on star_labels (subscript R₁)
const starred = await xpict.render(xpict.mol("*C"), {
  star_labels: ["$R_1$"], // or "R_{1}"
});
// Real CXSMILES alias (literal R1 — no subscript)
const fromCx = await xpict.render(xpict.mol("*C |$R1;$|"));
Star labeled R₁
`star_labels: ["$R_1$"]` → R₁

Label markup · full option table in API overview.

Declarative document

Nested JSON — a strict subset of future PictSpec. Fields today are structure, id, color, shade, and star_labels. Richer diagram nodes stay under xpict.future until they graduate; every document must still validate as future PictSpec.

const [r] = await xpict.depict({
  type: "mol",
  smiles: "CCO",
  color: "#0b6e4f",
  shade: { atoms: [0.0, 0.2, 0.9], vmin: 0, vmax: 1 },
});
const svg = xpict.toSvg(r.scene);

const batch = await xpict.depict({
  type: "group",
  children: [
    { type: "mol", smiles: "*c1ccccc1Cl", star_labels: ["$R_1$"] },
    { type: "mol", smiles: "c1ccccc1O" },
  ],
});
from xpict import render

svg = render({
    "type": "mol",
    "smiles": "CCO",
    "color": "#0b6e4f",
    "shade": {"atoms": [0.0, 0.2, 0.9], "vmin": 0.0, "vmax": 1.0},
})
use xpict::{depict, DepictSpec, MolNode};

let out = depict(&DepictSpec::Mol {
    smiles: Some("CCO".into()),
    color: Some("#0b6e4f".into()),
    ..Default::default()
})?;
let svg = out[0].to_svg();
Ethanol with shading
Ethanol with shading
Markush R1
`*c1ccccc1Cl |$R1;;;;;$|`

More depictions

Full gallery JS align demo

Packages

Language Package Docs
Python xpict API
JavaScript @xenosite/xpict API
Rust xpict / xpict-core API