@cancentus/smiles-drawer
v3.3.2
Published
A SMILES drawer and parser. Generate molecular structure depictions in pure JavaScript.
Readme
SmilesDrawer 2.0
Fork notice. This is a fork of reymond-group/smilesDrawer, created by Daniel Probst and the Reymond Group at the University of Berne. All credit for the original library goes to the upstream authors.
The upstream website, playground and hosted documentation are not part of this fork; this README and CONTRIBUTING.md are the documentation.
No server, no images, no templates, just a SMILES 😊
Current Version: 3.3.2
Examples in Specific Frameworks
<!--file:Molecule.svlete-->
<!--Tested against v2.1.7 of smiles-drawer-->
<script>
import { afterUpdate } from "svelte";
import SmilesDrawer from "smiles-drawer";
export let smiles = "";
const SETTINGS = {
width: 300,
height: 200,
};
let drawer = new SmilesDrawer.SvgDrawer(SETTINGS);
let svgElement;
afterUpdate(() => {
SmilesDrawer.parse(smiles, function (tree) {
drawer.draw(tree, svgElement, "light");
});
});
</script>
<div>
<svg bind:this={svgElement} data-smiles={smiles} />
</div>
<style>
svg {
width: 300px;
height: 200px;
}
</style>
<!--usage-->
<Molecule smiles="CCCO" />Please cite
If you use this code or application, please cite the original paper published by the Journal of Chemical Information and Modeling: 10.1021/acs.jcim.7b00425
Legacy Documentation
As of now, there is full backwards compatibility.
Examples
A very simple JSFiddle example can be found here. This example shows the SmilesDrawer.apply() functionality which draws the structure for every canvas element with a data-smiles attribute. E.g. <canvas data-smiles="C1CCCCC1"></canvas>
Installation
Published to npm under the @cancentus org:
npm install @cancentus/smiles-drawer
bun add @cancentus/smiles-drawerThe minified bundles and type declarations are committed, so this also works installed straight from Git (npm install github:Cancentus/smilesDrawer) with no build step and no trustedDependencies entry. Everything else in dist is generated — clone the repo and run npm install (or npm run release) to build the full set.
Building Smiles Drawer
See CONTRIBUTING.md for how to get the code and build your own custom version.
Getting Started
To get a simple input box which lets the user enter a SMILES and then display it in a canvas, the following minimal example is sufficient. In order to have nice consistent font rendering you have to include the droid sans font from google fonts.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>Smiles Drawer Example</title>
<meta name="description" content="A minimal smiles drawer example." />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
href="https://fonts.googleapis.com/css?family=Droid+Sans:400,700"
rel="stylesheet"
/>
</head>
<body>
<input id="example-input" name="example-input" />
<canvas id="example-canvas" width="500" height="500"></canvas>
<script src="https://unpkg.com/@cancentus/smiles-drawer@2/dist/smiles-drawer.min.js"></script>
<script>
let input = document.getElementById("example-input");
let options = {};
// Initialize the drawer to draw to canvas
let smilesDrawer = new SmilesDrawer.Drawer(options);
// Alternatively, initialize the SVG drawer:
// let svgDrawer = new SmilesDrawer.SvgDrawer(options);
input.addEventListener("input", function() {
// Clean the input (remove unrecognized characters, such as spaces and tabs) and parse it
SmilesDrawer.parse(input.value, function(tree) {
// Draw to the canvas
smilesDrawer.draw(tree, "example-canvas", "light", false);
// Alternatively, draw to SVG:
// svgDrawer.draw(tree, 'output-svg', 'dark', false);
});
});
</script>
</body>
</html>See the example folder for a more complete example.
Options
The options are supplied to the constructor as shown in the example above.
let options = { ... };
let smilesDrawer = new SmilesDrawer.Drawer(options);The following options are available:
| Option | Identifier | Data Type | Default Value |
| --------------------------------------------------------------- | --------------------------- | ----------------------------------- | ------------- |
| Drawing width | width | number | 500 |
| Drawing height | height | number | 500 |
| Bond thickness | bondThickness | number | 1.0 |
| Bond length | bondLength | number | 30 |
| Short bond length (e.g. double bonds) as a fraction of bond length | shortBondLength | number | 0.8 |
| Bond spacing (e.g. space between double bonds) | bondSpacing | number | 0.17 * 30 |
| Atom Visualization | atomVisualization | string ['default', 'balls', 'none'] | 'default' |
| Large Font Size (in pt for elements) | fontSizeLarge | number | 11 |
| Small Font Size (in pt for numbers) | fontSizeSmall | number | 3 |
| Padding | padding | number | 10.0 |
| Show explicit carbons | showCarbons | string ['none', 'default', 'terminal', 'acyclic', 'all'] | 'default' |
| Show terminal carbons (deprecated; use showCarbons) | terminalCarbons | boolean | false |
| Show explicit hydrogens | explicitHydrogens | boolean | true |
| Overlap sensitivity | overlapSensitivity | number | 0.42 |
| # of overlap resolution iterations | overlapResolutionIterations | number | 1 |
| Draw concatenated terminals and pseudo elements | compactDrawing | boolean | true |
| Draw isomeric SMILES if available | isomeric | boolean | true |
| Font family | fontFamily | string | 'Arial, Helvetica, sans-serif' |
| Debug (draw debug information to canvas) | debug | boolean | false |
| Color themes | themes | object | see below |
The default options are defined as follows:
{
width: 500,
height: 500,
bondThickness: 1.0,
bondLength: 30,
shortBondLength: 0.8,
bondSpacing: 0.17 * 30,
atomVisualization: 'default',
isomeric: true,
debug: false,
showCarbons: 'default',
terminalCarbons: false,
explicitHydrogens: true,
overlapSensitivity: 0.42,
overlapResolutionIterations: 1,
compactDrawing: true,
fontFamily: 'Arial, Helvetica, sans-serif',
fontSizeLarge: 11,
fontSizeSmall: 3,
padding: 10.0,
themes: {
dark: {
C: '#ffffff',
O: '#e74c3c',
N: '#3498db',
F: '#27ae60',
CL: '#16a085',
BR: '#d35400',
I: '#8e44ad',
P: '#d35400',
S: '#f1c40f',
B: '#e67e22',
SI: '#e67e22',
H: '#aaaaaa',
BACKGROUND: '#141414'
},
light: {
C: '#222222',
O: '#e74c3c',
N: '#3498db',
F: '#27ae60',
CL: '#16a085',
BR: '#d35400',
I: '#8e44ad',
P: '#d35400',
S: '#f1c40f',
B: '#e67e22',
SI: '#e67e22',
H: '#666666',
BACKGROUND: '#ffffff'
}
}
};Additional built-in themes include oldschool, solarized, and solarized-dark.
showCarbons controls when explicit carbon labels are drawn:
"none"never labels plain carbons;"default"is the usual skeletal notation;"terminal"matches the formerterminalCarbons: truebehavior (explicit labels on terminal carbons such as methyl groups);"acyclic"labels every carbon that is not part of a ring;"all"labels every carbon, including ring atoms.
If showCarbons is "default" and terminalCarbons is true, the effective mode is "terminal" (legacy compatibility until v3.0).
MiniViewer
SmilesDrawer.MiniViewer is a small, click-to-enlarge structure viewer for the case where
many structures are shown at once (a table, a list of results): it draws compactly by
default (no explicit hydrogens, small canvas) and opens a standard-size view in a modal
<dialog> when clicked or activated via keyboard. The dialog fades in/out (300ms) and its
border color is the inverse of the resolved theme background, so it reads clearly against
whatever page it's opened over.
const viewer = new SmilesDrawer.MiniViewer(document.getElementById('mini'), {
theme: 'light',
expandedOptions: { width: 500, height: 500 }, // options for the enlarged dialog view
});
viewer.draw('CC(=O)Oc1ccccc1C(=O)O');The constructor takes the host element and an options object:
| Option | Description |
| --- | --- |
| miniOptions/expandedOptions | Molecule options merged over the mini preset / used for the enlarged view. |
| theme | Theme name, as passed to draw() (default 'light'). |
| values/dataset | An AtomValueOverlay bundle and which of its datasets to label with, applied to both views. |
| showControls | Whether to build the mini tile's H/values icon rail and the expanded dialog's own "Show all H"/"Show values" bar (default true). Set false when the host already has its own H/values UI. |
| onRender | (svg, {mode, drawer}) => void, called after every draw (mode is 'mini' or 'expanded'), before the tooltip attaches. For host-specific post-processing that a values bundle can't express — e.g. a bespoke value overlay with its own positioning/coloring rules. |
| onError | (err) => void, called if drawing fails. |
| expandable | Default true: click/Enter/Space opens the expanded view in a modal dialog. Set false for a host that already has its own dialog around the structure (e.g. a row-click preview) — the expanded view (bigger size, full toggle bar) then renders directly into the host element instead, with no click affordance and no dialog. |
Call destroy() to remove its listeners and dialog.
Mini tile controls
When showControls is true (the default), the mini tile itself carries a small, always-
visible icon rail pinned to its top-left corner — independent of the enlarged dialog's own
checkbox bar, and with its own toggle state:
- H toggles carbon labels showing implicit hydrogen counts (
showCarbons: 'all'), same as the dialog's "Show all H" checkbox. - # toggles the
valuesoverlay, and appears only when avaluesbundle with at least one dataset was passed. With more than one dataset, each click cycles to the next dataset, then off, then back to the first — there's no<select>on the rail; the button's tooltip names the active dataset (e.g. "Values: pKa (uni)") or "Values: off".
While values are shown, the active dataset's name is also printed as plain text in the tile's bottom-left corner, so which of several datasets is on screen is readable without hovering.
Both toggles redraw only the mini tile and never open the dialog. The rail and the caption sit a fixed 10px off the host element's corners regardless of any padding it sets, matching the inset Mol*'s viewport controls use — so a 2D tile and a Mol* viewer side by side line up.
Atom Tooltips and Value Overlays
SmilesDrawer.AtomTooltip and SmilesDrawer.AtomValueOverlay add per-atom hover info and
labels to an already-drawn SVG — MiniViewer uses both internally for its enlarged view, but
either can be used standalone against any SVG produced by SvgDrawer/Drawer.
AtomValueOverlay.parseAtomValueBundle(json) normalizes a plain values payload or a pKa
prediction payload (auto-detected) into an AtomValueBundle:
// Generic shape:
// { datasets: { <key>: { label, entries: [{ atom_index, parts: [{ text, color?, title? }] }] } } }
// pKa shape (from a pKa prediction backend) is auto-detected too.
const bundle = SmilesDrawer.AtomValueOverlay.parseAtomValueBundle(json);Passing a bundle (plus which dataset key to label with) to MiniViewer is normally enough —
it calls AtomValueOverlay.apply()/fitViewBoxToBundle() for you. For a standalone SVG, call
those directly, or attach hover rows for all of a bundle's datasets with AtomTooltip:
const tooltip = new SmilesDrawer.AtomTooltip(svgElement, { atomValueBundle: bundle });
tooltip.attach();
// tooltip.destroy() when the SVG is removed.parsePkaDatasets(json) is the pKa-specific path parseAtomValueBundle() delegates to when it
detects that shape; call it directly only if the payload is already known to be pKa-shaped.
RDKit CoordGen Layout
smilesDrawer never depends on @rdkit/rdkit itself — the host loads the module (and its
.wasm asset) and registers it once via SmilesDrawer.setRdkit(module). Once registered,
SmiDrawer.drawMolecule() (and thus SmiDrawer.apply()) automatically lay molecules out
with RDKit's CoordGen algorithm instead of smilesDrawer's own, with no other code changes.
import initRDKitModule from '@rdkit/rdkit';
const rdkitModule = await initRDKitModule();
SmilesDrawer.setRdkit(rdkitModule);For lower-level control — e.g. passing a layout into SmiDrawer.drawFromLayout(), or as the
presetLayout argument to SvgDrawer.draw()/Drawer.draw() — call
SmilesDrawer.layoutFromSmiles(smiles, module?) directly. It returns null (never throws) if
no module is registered, the SMILES is invalid, or RDKit otherwise fails, so callers should
fall back to the automatic layout in that case.
Usage
An instance of SmilesDrawer is able to draw to multiple targets. Initialize SmilesDrawer once for each set of options (you would initialize two different objects if you were to draw in two different sizes).
let smilesDrawer = new SmilesDrawer.Drawer({ width: 250, height: 250 });In order to depict a SMILES string it has to be parsed using SmilesDrawer's SMILES parser, which is encapsulated in the static function SmilesDrawer.parse() where the first argument is the SMILES string and the second argument a callback for a successful parsing. The third argument provides a way to handle errors using a callback.
SmilesDrawer.parse('C1CCCCC1', function (tree) {
smilesDrawer.draw(tree, 'output-canvas', 'light', false);
}, function (err) {
console.log(err);
}The function smilesDrawer.draw() requires two and accepts up to four arguments. The first argument is the parse tree returned by the parse function (through the callback), the second is the id of a HTML canvas element on which the structure will be drawn. The two optional arguments are whether to use the light or dark theme (defaults to 'light') and whether to only compute properties such as ring count, hac, etc. and not depict the structure (defaults to false).
API
The SmilesDrawer object exposes methods that can be used for purposes other than drawing chemical structures.
| Method | Description | Returns |
| ----------------------- | --------------------------------------------------------------------------------- | -------- |
| getMolecularFormula() | Returns the molecular formula, eg. C22H30N6O4S, of the currently loaded molecule. | String |
Bridged Rings
Bridged rings are positioned using the Kamada–Kawai algorithm. If there is a bridged ring in the molecule, explicitly defined aromatic rings are not drawn with a circle inside the ring, but with dashed gray lines where double bonds would be.
Contributors
Thank you for contributing:
- SRI International's CSE group (For the excellent SVG support)
- ohardy
- Kevin Burk
- Alejandro Flores
- Daniel Probst
