npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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-drawer

The 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 former terminalCarbons: true behavior (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 values overlay, and appears only when a values bundle 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: