Skip to content

WASM / JavaScript API

Installation

npm install renkin

Browser (ES Module)

<script type="module">
  import init, { find_routes, version } from './node_modules/renkin/renkin.js';

  await init();
  console.log('RENKIN version:', version());

  const raw = find_routes(
    "CC(=O)Oc1ccccc1C(=O)O",  // Aspirin
    5,   // max depth
    3,   // max routes
    0    // beam width (0 = unlimited)
  );
  const result = JSON.parse(raw);
  console.log('Routes found:', result.routes_found);
</script>

Browser and bundler usage

The npm package is currently built with wasm-pack build --target web.

Supported:

  • Native browser ES modules
  • Vite
  • Webpack
  • Rollup and compatible bundlers

Not currently supported:

  • Plain Node.js require()
  • Direct Node.js execution without a bundler

To exercise the WASM API from a plain Node.js script (not through a bundler), build a --target nodejs package from source instead — see Minimal Node.js Example below, which is verified this way, not against the published npm package.

find_routes

function find_routes(
  target: string,     // Target molecule SMILES
  depth: number,      // Maximum retrosynthetic depth
  max_routes: number, // Maximum routes to return
  beam_width: number  // A* beam width (0 = unlimited)
): string  // JSON-encoded result

WASM always uses the compiled-in default rule set (22 hand-crafted rules) and building blocks — there is no way to load an external templates file or custom building blocks list from the WASM entry point (unlike the CLI/Python bindings). See Rust API or Python API for --templates/templates_path support.

Return value (JSON):

interface Result {
  routes_found: number;
  routes: Route[];
}

interface Route {
  depth: number;
  score: number;
  confidence: number;
  success_probability: number;
  convergency: number;
  route_cost: number;
  building_blocks: string[];
  steps: Step[];
}

interface Step {
  target: string;         // SMILES of target at this step
  rule: string;           // reaction rule name
  template_id: string;    // stable template identity (rule:<name> / smirks-sha256:<hex>)
  precursors: string[];   // SMILES of precursor molecules
  step_confidence: number;
  atom_economy_status: string; // "normal" / "above_expected_range" / "not_evaluable" (always present)
  // conditions / atom_economy / atom_economy_raw_percent / procedure_hint /
  // reaction_family / metadata_source / metadata_scope / evidence are present
  // when applicable and simply absent from the JSON otherwise
}

audit_route_v2

function audit_route_v2(
  content: string,    // Route export JSON text (RENKIN, AiZynthFinder, or Syntheseus)
  format: string,      // "auto" | "renkin" | "aizynthfinder" | "syntheseus"
  stockText: string,    // "" for no stock, else one SMILES per line (.smi-style)
  policy: string         // "informational" | "standard" | "strict"
): string  // JSON-encoded AuditRouteReport, or {"error": "..."}

The browser counterpart to renkin audit-route (see Audit Reproducibility and Compatibility Contract for the full AuditRouteReport/audit_manifest shape, and what each policy value means) — calls the identical bridge::build_audit_route_report_with_policy pipeline the CLI uses, so a route audited in the browser gets exactly the same verdict the CLI would produce for the same input and policy. Unlike the CLI, content must already be plain JSON text — there is no gzip support in the browser (a paste or file upload never needs it). policy controls only how each route's status is derived from findings already collected — never which findings are detected or reported.

import init, { audit_route_v2 } from './node_modules/renkin/renkin.js';

await init();
const routeJson = JSON.stringify({
  target: "CCOC(=O)c1ccccc1",
  routes: [{
    steps: [{ target: "CCOC(=O)c1ccccc1", precursors: ["CCO", "O=C(O)c1ccccc1"], template_id: "co_aliphatic_cleavage" }],
    building_blocks: ["CCO", "O=C(O)c1ccccc1"],
  }],
});
const report = JSON.parse(audit_route_v2(routeJson, "auto", "", "strict"));
console.log(report.routes[0].status); // "pass" | "fail" | "partial"

Also available from the Live Playground's [ Audit a Route ] tab — paste or upload a route (and optionally a stock list) with a policy selector, entirely client-side.

audit_route

function audit_route(content: string, format: string, stockText: string): string

The original (v0.28.0) 3-argument export, kept unchanged for backward compatibility — a thin policy: "standard" wrapper around audit_route_v2. New code should call audit_route_v2 directly; audit_route exists only so a build predating v0.29.0's policy parameter keeps working exactly as before.

version

function version(): string

Returns the RENKIN version string (e.g., "0.35.0").

Minimal Node.js Example (CI-verified)

This example runs against a package built locally with wasm-pack build --target nodejs — a different build target from the published npm package (--target web, browser/bundler only; see Browser and bundler usage above). It's the from-source path for using RENKIN's WASM bindings in a plain Node.js script; npm install renkin alone does not give you this.

examples/quickstart.mjs is run against a wasm-pack build --target nodejs output as part of CI, so this call shape can't silently drift from the real API:

// RENKIN WASM/JavaScript quickstart. Run against a
// `wasm-pack build --target nodejs` output as part of CI (see
// .github/workflows/ci.yml) so this example can never silently drift from
// the real `find_routes`/`audit_route` API.
import { find_routes, audit_route } from "../pkg/renkin.js";

const target = "CC(=O)Oc1ccccc1C(=O)O";
const result = JSON.parse(find_routes(target, 5, 3, 0));

console.log(`Routes found: ${result.routes_found}`);
for (const route of result.routes) {
  console.log(`Route (depth ${route.depth}):`);
  for (const step of route.steps) {
    console.log(`  ${step.target} -> ${step.precursors.join(" + ")}`);
    console.log(`  via ${step.rule}`);
  }
}

// Audit the first found route -- same "Plan a Route" -> "Audit a Route"
// flow the playground offers, via the identical pipeline `renkin
// audit-route` uses on the CLI.
if (result.routes.length > 0) {
  const route = result.routes[0];
  const routeInput = JSON.stringify({
    target,
    routes: [{
      steps: route.steps.map((s) => ({
        target: s.target,
        precursors: s.precursors,
        template_id: s.template_id,
      })),
      building_blocks: route.building_blocks,
    }],
  });
  const auditReport = JSON.parse(audit_route(routeInput, "renkin", ""));
  console.log(`Audit verdict: ${auditReport.routes[0].status}`);
}

Live Playground

An interactive playground is available at /playground/.

The playground runs entirely in WebAssembly in your browser — no network calls, no server.

Example: React Integration

import { useEffect, useState } from 'react';

function RetrosynthesisWidget({ smiles }) {
  const [routes, setRoutes] = useState(null);
  const [wasmReady, setWasmReady] = useState(false);

  useEffect(() => {
    import('renkin').then(async (mod) => {
      await mod.default();
      setWasmReady(true);
    });
  }, []);

  useEffect(() => {
    if (!wasmReady || !smiles) return;
    import('renkin').then((mod) => {
      const raw = mod.find_routes(smiles, 5, 3, 0);
      setRoutes(JSON.parse(raw));
    });
  }, [wasmReady, smiles]);

  if (!routes) return <div>Loading...</div>;
  return <div>Found {routes.routes_found} routes</div>;
}