Ketcher is an open-source web-based chemical structure editor incorporating high performance, good portability, light weight, and ability to easily integrate into a custom web-application. Ketcher is designed for chemists, laboratory scientists and technicians who draw structures and reactions.
- ⚡️ Fast 2D structure representation that satisfies common chemical drawing standards
- 💠 3D structure visualization
- 📝 Template library (including custom and user's templates)
- 🔩 Add atom and bond basic properties and query features, add aliases and Generic groups
- 🌀 Stereochemistry support during editing, loading, and saving chemical structures
- ➿ Storing history of actions, with the ability to rollback to previous state
- 💾 Ability to load and save structures and reactions in MDL Molfile or RXN file format, InChI String, ChemAxon Extended SMILES, ChemAxon Extended CML file formats
- 🔬 Zoom in/out, hotkeys, cut/copy/paste
- 🔮 OCR - ability to recognize structures at pictures (image files) and reproduce them
- 📋 Copy and paste between different chemical editors
- 🛠️ Settings support (Rendering, Displaying, Debugging)
- 📷 Use of SVG to achieve best quality in-browser chemical structure rendering
- Atom Tool, Bond Tool, and Template Tool to draw and edit structures
- Aromatize/De-aromatize Tool
- Calculate CIP Descriptors Tool
- Structure Check Tool
- MW and Structure Parameters Calculate Tool
- Select, modify, and erase connected and unconnected atoms and bonds using Selection Tool, or using Shift key
- Advanced Structure Clean up Tool (+ stereochemistry checking and structure layout)
- Simple Structure Clean up Tool (checks bonds length, angles and spatial arrangement of atoms)
- Easy to use R-Group and S-Group tools (Generic, Multiple group, SRU polymer, peratom, Data S-Group)
- Reaction Tool (reaction generating, manual and automatic atom-to-atom mapping)
- Flip/Rotate Tool
At this moment Ketcher can be embedded into your application in two ways:
- as ready-to-run application (to find desired version please look at Assets block of releases). The application can be injected as IFrame or a separate page.
- as a react component library
npm install ketcher-core ketcher-reactimport { Editor } from 'ketcher-react';
import { StandaloneStructServiceProvider } from 'ketcher-standalone';
import 'ketcher-react/dist/index.css';
const structServiceProvider = new StandaloneStructServiceProvider();
function App() {
return (
<Editor
staticResourcesUrl="/public"
structServiceProvider={structServiceProvider}
onInit={(ketcher) => {
window.ketcher = ketcher;
}}
/>
);
}Look at the following link for details.
You can find the instruction for service installation here.
| Project | Status | Description |
|---|---|---|
| ketcher-core | Core functionality: domain, shared services, functions and interface declarations | |
| ketcher-standalone | Contains only the functionality necessary to start Ketcher in standalone mode | |
| ketcher-react | Package contains only the functionality necessary to define components. | |
| ketcher-macromolecules | Package contains the macromolecules editor functionality and UI components |
Ketcher uses Miew-React for viewing and editing data in 3D.
You can find the latest version of Miew-React here. The last checked version - 1.0.0.
Starting with version 3.0, Ketcher supports a new control in the top toolbar that allows switching to macromolecules editing mode. If you prefer having only small molecules editing mode available, you can remove the mode switcher from the toolbar by passing disableMacromoleculesEditor property to the Editor component.
import { Editor } from 'ketcher-react';
const App = () => {
return (
<Editor
{/* ...rest of the properties */}
disableMacromoleculesEditor
/>
);
};Please refer to the example/src/App.tsx file for a complete example of how to integrate Ketcher editor into your application.
- Structure Export Methods
- Structure Import Methods
- Editor Operations
- View Control
- Settings Management
- Event System
- React Component API
- Macromolecules Mode
- Service Providers
- Supported Formats
- Usage Examples
- Internal Services (Advanced)
- Error Handling
All export methods return Promise<string> with the structure in the requested format.
getSmiles(isExtended?: boolean): Promise<string>Returns SMILES (Simplified Molecular Input Line Entry System) representation.
Parameters:
isExtended(optional) - Use extended SMILES format (default:false)
Throws: Error in macromolecules mode
Example:
const smiles = await ketcher.getSmiles();
// Returns: "C1=CC=CC=C1"
const extended = await ketcher.getSmiles(true);
// Returns extended SMILES with stereochemistrygetExtendedSmiles(): Promise<string>Alias for getSmiles(true).
getMolfile(molfileFormat?: 'v2000' | 'v3000'): Promise<string>Returns MDL MOL file format.
Parameters:
molfileFormat(optional) - MOL file version:'v2000','v3000', or auto-detect
Throws: Error if structure contains reaction arrows (use getRxn instead)
Example:
const mol = await ketcher.getMolfile('v3000');getRxn(molfileFormat?: 'v2000' | 'v3000'): Promise<string>Returns RXN (Reaction) file format.
Parameters:
molfileFormat(optional) - RXN file version (default:'v2000')
Throws:
- Error if no reaction arrows present
- Error in macromolecules mode
Example:
const rxn = await ketcher.getRxn('v3000');getKet(): Promise<string>Returns KET (Ketcher JSON) format - the native internal format.
Works in both micro and macromolecules modes.
Example:
const ket = await ketcher.getKet();
const ketObj = JSON.parse(ket);getSmarts(): Promise<string>Returns SMARTS (SMILES Arbitrary Target Specification) representation.
Throws: Error in macromolecules mode
getCml(): Promise<string>Returns CML (Chemical Markup Language) format.
Throws: Error in macromolecules mode
getSdf(molfileFormat?: 'v2000' | 'v3000'): Promise<string>Returns SDF (Structure-Data File) format.
Parameters:
molfileFormat(optional) - Version (default:'v2000')
Throws: Error in macromolecules mode
getRdf(molfileFormat?: 'v2000' | 'v3000'): Promise<string>Returns RDF (Reaction Data File) format.
Parameters:
molfileFormat(optional) - Version (default:'v2000')
Throws: Error in macromolecules mode
getCDXml(): Promise<string>Returns CDXML (ChemDraw XML) format.
Throws: Error in macromolecules mode
getCDX(): Promise<string>Returns CDX (ChemDraw binary) format as base64-encoded string.
Throws: Error in macromolecules mode
getInchi(withAuxInfo?: boolean): Promise<string>Returns InChI (IUPAC International Chemical Identifier) representation.
Parameters:
withAuxInfo(optional) - Include auxiliary information (default:false)
Example:
const inchi = await ketcher.getInchi();
// Returns: "InChI=1S/C6H6/c1-2-4-6-5-3-1/h1-6H"getInChIKey(): Promise<string>Returns InChI Key (hashed InChI).
Example:
const key = await ketcher.getInChIKey();
// Returns: "UHOVQNZJYSORNB-UHFFFAOYSA-N"getFasta(): Promise<string>Returns FASTA format for biological sequences.
Available in macromolecules mode.
getSequence(format: '1-letter' | '3-letter'): Promise<string>Returns sequence in 1-letter or 3-letter amino acid/nucleotide format.
Parameters:
format- Sequence format:'1-letter'or'3-letter'
Example:
const seq = await ketcher.getSequence('1-letter');
// Returns: "ACDEFG"getIdt(): Promise<string>Returns IDT (Integrated DNA Technologies) format.
getAxoLabs(): Promise<string>Returns AxoLabs format.
setMolecule(structure: string, options?: SetMoleculeOptions): Promise<void>Loads structure onto canvas, replacing existing structure.
Parameters:
structure- Structure string (format auto-detected)options(optional) - Import options
SetMoleculeOptions:
interface SetMoleculeOptions {
position?: { x: number; y: number }; // Top-left corner in Angstroms (Y increases upward)
needZoom?: boolean; // Zoom to fit (default: true)
}Automatically centers and zooms to fit structure.
Example:
// Load SMILES
await ketcher.setMolecule('C1=CC=CC=C1');
// Load at specific position
await ketcher.setMolecule('CCO', {
position: { x: 5, y: 5 },
needZoom: false
});
// Load MOL file
const molfile = `...`;
await ketcher.setMolecule(molfile);
// Load KET
const ket = JSON.stringify({ root: { nodes: [...] } });
await ketcher.setMolecule(ket);addFragment(structure: string, options?: SetMoleculeOptions): Promise<void>Adds structure to canvas without removing existing content.
Parameters: Same as setMolecule
Example:
await ketcher.setMolecule('C1=CC=CC=C1');
await ketcher.addFragment('CCO', { position: { x: 10, y: 0 } });setHelm(helmStr: string): Promise<void>Loads HELM (Hierarchical Editing Language for Macromolecules) notation structure.
Example:
await ketcher.setHelm('PEPTIDE1{A.G.F}$$$$V2.0');layout(): Promise<void>Performs automatic layout algorithm using Indigo service for smart positioning.
Throws: Error in macromolecules mode
Example:
await ketcher.layout();circularLayoutMonomers(): Promise<void>Arranges monomers in circular layout.
Only available in macromolecules mode.
calculate(options?: CalculateData): Promise<CalculateResult>Calculates molecular properties.
Returns:
interface CalculateResult {
'molecular-weight': string;
'most-abundant-mass': string;
'monoisotopic-mass': string;
'gross': string; // Molecular formula
'mass-composition': string;
}Throws: Error in macromolecules mode
Example:
const props = await ketcher.calculate();
console.log('Molecular weight:', props['molecular-weight']);
console.log('Formula:', props['gross']);recognize(image: Blob, version?: string): Promise<Struct>OCR: Recognizes chemical structure from image.
Parameters:
image- Image blob (PNG, JPG, etc.)version(optional) - OCR version
Returns: Struct object (internal structure representation)
Throws: Error in macromolecules mode
Example:
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const struct = await ketcher.recognize(file);generateImage(data: string, options: GenerateImageOptions): Promise<Blob>Generates image from structure string.
Parameters:
data- Structure string (any supported format)options- Image generation options
GenerateImageOptions:
interface GenerateImageOptions {
outputFormat: 'png' | 'svg';
backgroundColor?: string; // Hex color (default: transparent)
bondThickness?: number; // Line thickness in pixels
}Example:
const blob = await ketcher.generateImage('C1=CC=CC=C1', {
outputFormat: 'png',
backgroundColor: '#FFFFFF',
bondThickness: 2
});
// Create download link
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'molecule.png';
a.click();exportImage(format: 'svg', params?: ExportImageParams): voidExports current canvas as image file (downloads automatically).
Parameters:
format- Currently only'svg'supportedparams(optional) - Export parameters
ExportImageParams:
interface ExportImageParams {
margin?: number; // Margin around structure in pixels
}Example:
ketcher.exportImage('svg', { margin: 10 });setZoom(value: number): voidSets zoom level.
Parameters:
value- Zoom level (range: 0.2 to 4.0)
Example:
ketcher.setZoom(1.5); // 150% zoomsetMode(mode: 'flex' | 'snake' | 'sequence'): voidSets macromolecules layout mode.
Parameters:
mode- Layout mode:'flex'- Flexible layout (default)'snake'- Snake (linear) layout'sequence'- Sequence editor mode
Only available in macromolecules mode.
Example:
ketcher.setMode('sequence');setSettings(settings: Record<string, string | boolean>): voidUpdates editor settings.
Supported Settings:
| Setting | Type | Description |
|---|---|---|
general.dearomatize-on-load |
boolean |
Dearomatize structures on load |
ignoreChiralFlag |
boolean |
Ignore chiral flag from MOL files |
disableQueryElements |
string[] |
Array of query elements to disable (e.g., ['Pol', 'CYH']) |
bondThickness |
number |
Bond line thickness in pixels |
disableCustomQuery |
boolean |
Disable custom query features |
persistMonomerLibraryUpdates |
boolean |
Persist monomer library changes to localStorage |
Example:
ketcher.setSettings({
'general.dearomatize-on-load': false,
'ignoreChiralFlag': true,
'bondThickness': 2,
'disableQueryElements': ['Pol', 'CYH']
});get settings(): Record<string, any>Returns current allowed settings.
Example:
const currentSettings = ketcher.settings;
console.log(currentSettings);Ketcher allows to subscribe on events to react to changes in the editor.
Subscribe() - method that allows you to attach an event handler function for event.
ketcher.editor.subscribe(eventName, handler)
eventName: string - event type, such as 'change'.
handler: function - a function to execute when the event is triggered.
A new object that has one field 'handler'.
Unsubscribe() - method that removes event handler.
ketcher.editor.unsubscribe(eventName, subscriber)
eventName: string - one event type, such as 'change'.
subscriber: object - an object that is returned from the subscriber function.
undefined
Ketcher provides an event subscription mechanism for tracking editor changes and library updates.
Subscribe:
ketcher.editor.subscribe(
eventName: 'change',
handler: (eventData: ChangeEvent) => void
): SubscriptionUnsubscribe:
ketcher.editor.unsubscribe(
eventName: 'change',
subscription: Subscription
): voidEvent Data Structure:
The event data contains operation details:
interface ChangeEvent {
operation: string; // Operation type (see below)
[key: string]: any; // Additional operation-specific data
}| Operation | Example |
|---|---|
| Add atom | {operation: 'Add atom',id: 1,label: 'C',position: { x: 3.37, y: 6.03 }} |
| Move atom | {operation: 'Move atom',id: 1,position: { x: 0.02, y: 2.4 }} |
| Set atom attribute | {operation: 'Set atom attribute',id: 22,from: 0,to: 1,attribute: 'charge'} |
| Delete atom | {operation: 'Delete atom',id: 1,label: 'C',position: { x: 3.37, y: 6.03 }} |
| Add bond | {operation: 'Add bond',id: 31} |
| Move bond | {operation: 'Move bond'} |
| Set bond attribute | {operation: 'Set bond attribute'} |
| Delete bond | {operation: 'Delete bond',id: 31} |
| Move loop | {operation: 'Move loop'} |
| Add atom to s-group | {operation: 'Add atom to s-group',sGroupId: 0,atomId: 16} |
| Remove atom from s-group | {operation: 'Remove atom from s-group'} |
| Create s-group | {operation: 'Create s-group',sGroupId: 0,type: 'GEN'} |
| Set s-group attribute | {operation: 'Set s-group attribute'} |
| Delete s-group | {operation: 'Delete s-group',sGroupId: 0,type: 'GEN'} |
| Add s-group to hierarchy | {operation: 'Add s-group to hierarchy'} |
| Delete s-group from hierarchy | {operation: 'Delete s-group to hierarchy'} |
| Set r-group attribute | {operation: 'Set r-group attribute'} |
| R-group fragment | {operation: 'R-group fragment',id: 1} |
| Update | {operation: 'Update'} |
| Restore | {operation: 'Restore'} |
| Add rxn arrow | {operation: 'Add rxn arrow',id: 1,position: { x: 4.02, y: 4.83 }} |
| Delete rxn arrow | {operation: 'Delete rxn arrow',id: 1,position: { x: 4.02, y: 4.83 }} |
| Move rxn arrow | {operation: 'Move rxn arrow',id: 1,position: { x: 0.07, y: 1.18 }} |
| Add rxn plus | {operation: 'Add rxn plus'} |
| Delete rxn plus | {operation: 'Delete rxn plus'} |
| Move rxn plus | {operation: 'Move rxn plus'} |
| Move s-group data | {operation: 'Move s-group data'} |
| Load canvas | {operation: 'Load canvas'} |
| Align descriptors | {operation: 'Align descriptors'} |
| Add simple object | {operation: 'Add simple object',id: 1,type: 'circle'} |
| Move simple object | {operation: 'Move simple object',id: 1,position: { x: 0.07, y: 1.18 }} |
| Resize simple object | {operation: 'Resize simple object',id: 1} |
| Delete simple object | {operation: 'Delete simple object',id: 1,type: 'circle'} |
| Restore descriptors position | {operation: 'Restore descriptors position'} |
| Add fragment | {operation: 'Add fragment',id: 1} |
| Delete fragment | {operation: 'Delete fragment',id: 1} |
| Add fragment stereo flag | {operation: 'Add fragment stereo flag'} |
| Add stereo atom to fragment | {operation: 'Add stereo atom to fragment',atomId: 1,fragId: 2} |
| Delete stereo atom from fragment | {operation: 'Delete stereo atom from fragment',atomId: 1,fragId: 2} |
| Move enhanced flag | {operation: 'Move enhanced flag'} |
Example:
const subscription = ketcher.editor.subscribe('change', (event) => {
console.log('Operation:', event.operation);
if (event.operation === 'Add atom') {
console.log('Added atom:', event.label, 'at', event.position);
}
if (event.operation === 'Set atom attribute') {
console.log('Changed', event.attribute, 'from', event.from, 'to', event.to);
}
});
// Later: unsubscribe
ketcher.editor.unsubscribe('change', subscription);The 'libraryUpdate' event is triggered when some change in monomers library happens. For example when user creates new monomer using "Create monomer" tool, or library changes after the specific API call.
ketcher.editor.subscribe('libraryUpdate', (eventData) => {
console.log('Library updated:', eventData);
});eventData is a string in SDF format according to specification
Custom buttons emit events via the event bus:
ketcher.eventBus.on(
eventName: 'CUSTOM_BUTTON_PRESSED',
handler: (buttonId: string) => void
): voidExample:
ketcher.eventBus.on('CUSTOM_BUTTON_PRESSED', (buttonId) => {
console.log('Custom button clicked:', buttonId);
if (buttonId === 'export-to-database') {
exportToDatabase();
}
});The Editor component is the main React wrapper for Ketcher.
Import:
import { Editor } from 'ketcher-react';
import 'ketcher-react/dist/index.css';Props:
interface EditorProps {
staticResourcesUrl: string; // Required
structServiceProvider: StructServiceProvider; // Required
onInit?: (ketcher: Ketcher) => void;
errorHandler?: (message: string) => void;
buttons?: ButtonsConfig;
customButtons?: CustomButton[];
disableMacromoleculesEditor?: boolean;
monomersLibraryUpdate?: string | JSON;
monomersLibraryReplace?: string | JSON;
}Required Props:
staticResourcesUrlstring- Path to static resources (SVG icons, templates)structServiceProviderStructServiceProvider- Service provider instance
Optional Props:
onInit(ketcher: Ketcher) => void- Callback when Ketcher initializeserrorHandler(message: string) => void- Error handler callbackbuttonsButtonsConfig- Configure toolbar button visibilitycustomButtonsCustomButton[]- Add custom toolbar buttonsdisableMacromoleculesEditorboolean- Hide macromolecules mode toggle (default:false)monomersLibraryUpdatestring | JSON- Update monomer library on initmonomersLibraryReplacestring | JSON- Replace monomer library on init
Configure visibility of built-in toolbar buttons:
type ButtonsConfig = {
[buttonName in ButtonName]?: { hidden?: boolean };
};Button Names: 'clear', 'open', 'save', 'undo', 'redo', 'cut', 'copy', 'paste', 'zoom-in', 'zoom-out', 'layout', 'clean', 'arom', 'dearom', 'cip', 'check', 'analyse', 'recognize', 'miew', 'settings', 'help', 'about', etc.
(See ketcher-react/src/script/ui/buttonsConfig.ts in the repo for an up-to-date list.)
Example:
<Editor
staticResourcesUrl="/public"
structServiceProvider={provider}
buttons={{
clear: { hidden: true },
miew: { hidden: true }
}}
/>Add custom toolbar buttons:
interface CustomButton {
id: string; // Unique button identifier
imageLink: string; // Path to button icon (SVG recommended)
title: string; // Tooltip text
}Example:
<Editor
staticResourcesUrl="/public"
structServiceProvider={provider}
customButtons={[
{
id: 'export-to-db',
imageLink: '/icons/database.svg',
title: 'Export to Database'
},
{
id: 'custom-tool',
imageLink: '/icons/tool.svg',
title: 'Custom Tool'
}
]}
onInit={(ketcher) => {
ketcher.eventBus.on('CUSTOM_BUTTON_PRESSED', (id) => {
if (id === 'export-to-db') {
exportToDatabase();
}
});
}}
/>Ketcher supports editing biological macromolecules (peptides, nucleic acids, etc.).
switchToMacromoleculesMode(): void
switchToMoleculesMode(): voidExample:
ketcher.switchToMacromoleculesMode();updateMonomersLibrary(
monomersData: string | JSON,
params?: UpdateMonomersLibraryParams
): Promise<void>Upserts monomers to built-in library (adds new, updates existing).
Parameters:
monomersData- Monomer data (KET or SDF format)params(optional) - Update parameters
UpdateMonomersLibraryParams:
interface UpdateMonomersLibraryParams {
format?: 'ket' | 'sdf'; // Input format (default: auto-detect)
shouldPersist?: boolean; // Save to localStorage (default: false)
needDispatchLibraryUpdateEvent?: boolean; // Trigger library update event (default: true)
}Example:
const monomersKet = JSON.stringify({
root: { nodes: [...] },
// ... monomer definitions
});
await ketcher.updateMonomersLibrary(monomersKet, {
format: 'ket',
shouldPersist: true
});replaceMonomersLibrary(
monomersData: string | JSON,
params?: UpdateMonomersLibraryParams
): Promise<void>Replaces entire monomer library (removes all existing monomers).
Parameters: Same as updateMonomersLibrary
Example:
await ketcher.replaceMonomersLibrary(monomersKet, {
format: 'ket',
shouldPersist: true
});getFasta()- Export FASTA formatgetSequence(format)- Export sequence (1-letter or 3-letter)getIdt()- Export IDT formatgetAxoLabs()- Export AxoLabs formatsetHelm(helmStr)- Import HELM notationcircularLayoutMonomers()- Circular layoutsetMode(mode)- Set layout mode ('flex', 'snake', 'sequence')
Ketcher uses a service provider pattern for chemical operations. Two implementations are available:
Uses Indigo Service (REST API) for server-side chemical operations.
Location: ketcher-core
Constructor:
constructor(
apiPath: string,
defaultOptions?: Record<string, any>,
customHeaders?: Record<string, string>
)Example:
import { RemoteStructServiceProvider } from 'ketcher-core';
const provider = new RemoteStructServiceProvider(
'http://localhost:8002',
{
'smart-layout': true,
'ignore-stereochemistry-errors': true
},
{
'Authorization': 'Bearer token123',
'Custom-Header': 'value'
}
);Default Options:
{
'smart-layout': true,
'ignore-stereochemistry-errors': true,
'mass-skip-error-on-pseudoatoms': false,
'gross-formula-add-rsites': true,
'aromatize-skip-superatoms': true,
'dearomatize-on-load': false,
'ignore-no-chiral-flag': false
}Uses Indigo WASM (client-side) for chemical operations without server dependency.
Location: ketcher-standalone
Constructor:
constructor()Example:
import { StandaloneStructServiceProvider } from 'ketcher-standalone';
const provider = new StandaloneStructServiceProvider();Note: WASM bundle is larger but provides offline capabilities.
Ketcher automatically detects the input format:
- MOL (V2000/V3000) - MDL Molfile
- RXN (V2000/V3000) - MDL Reaction file
- KET - Ketcher JSON format
- SMILES - Simplified Molecular Input Line Entry System
- Extended SMILES - SMILES with stereochemistry
- SMARTS - SMILES Arbitrary Target Specification
- InChI - IUPAC International Chemical Identifier
- CML - Chemical Markup Language
- CDXML - ChemDraw XML
- CDX - ChemDraw binary
- SDF - Structure-Data File
- RDF - Reaction Data File
- FASTA - Biological sequence format
- HELM - Hierarchical Editing Language for Macromolecules
- IDT - Integrated DNA Technologies
- AxoLabs - AxoLabs format
- Sequence - RNA/DNA/Peptide (1-letter and 3-letter)
All input formats plus:
- InChI Key - Hashed InChI identifier
- PNG - Portable Network Graphics image
- SVG - Scalable Vector Graphics image
// Load structure
await ketcher.setMolecule('C1=CC=CC=C1');
// Export to different formats
const smiles = await ketcher.getSmiles();
console.log('SMILES:', smiles); // "c1ccccc1"
const mol = await ketcher.getMolfile('v3000');
console.log('MOL:', mol);
const ket = await ketcher.getKet();
const ketObj = JSON.parse(ket);
console.log('KET:', ketObj);// Load first molecule
await ketcher.setMolecule('C1=CC=CC=C1', {
position: { x: 0, y: 0 },
needZoom: false
});
// Add second molecule
await ketcher.addFragment('CCO', {
position: { x: 10, y: 0 },
needZoom: false
});
// Add reaction arrow at x=15
// (Must be done through editor API or manually)await ketcher.setMolecule('C6H12O6'); // Glucose
const props = await ketcher.calculate();
console.log('Molecular Weight:', props['molecular-weight']);
console.log('Formula:', props['gross']);
console.log('Monoisotopic Mass:', props['monoisotopic-mass']);let atomCount = 0;
let bondCount = 0;
const subscription = ketcher.editor.subscribe('change', (event) => {
if (event.operation === 'Add atom') {
atomCount++;
console.log(`Atoms: ${atomCount}`);
}
if (event.operation === 'Add bond') {
bondCount++;
console.log(`Bonds: ${bondCount}`);
}
if (event.operation === 'Undo' || event.operation === 'Redo') {
console.log('History changed');
}
});
// Later: cleanup
ketcher.editor.unsubscribe('change', subscription);const smiles = 'CC(C)C1=CC=C(C=C1)C(C)C(=O)O'; // Ibuprofen
const imageBlob = await ketcher.generateImage(smiles, {
outputFormat: 'png',
backgroundColor: '#FFFFFF',
bondThickness: 2
});
// Display in browser
const img = document.createElement('img');
img.src = URL.createObjectURL(imageBlob);
document.body.appendChild(img);
// Or download
const a = document.createElement('a');
a.href = URL.createObjectURL(imageBlob);
a.download = 'ibuprofen.png';
a.click();const fileInput = document.getElementById('image-upload');
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
try {
const struct = await ketcher.recognize(file);
console.log('Structure recognized!');
// Structure is already loaded on canvas
const smiles = await ketcher.getSmiles();
console.log('Recognized SMILES:', smiles);
} catch (error) {
console.error('Recognition failed:', error);
}
});// Switch to macromolecules mode
ketcher.switchToMacromoleculesMode();
// Load peptide sequence
await ketcher.setMolecule('ACDEFGHIKLMNPQRSTVWY', {
needZoom: true
});
// Change layout
ketcher.setMode('sequence');
// Export as FASTA
const fasta = await ketcher.getFasta();
console.log(fasta);
// Export as HELM
const helm = await ketcher.getHelm();
console.log(helm);
// Get 1-letter sequence
const seq = await ketcher.getSequence('1-letter');
console.log('Sequence:', seq);// Define custom monomers in KET format
const customMonomers = {
root: { nodes: [] },
monomerTemplate_Custom1: {
type: 'monomerTemplate',
id: 'Custom1___CustomMonomer',
class: 'AminoAcid',
classHELM: 'PEPTIDE',
alias: 'Cst',
name: 'Custom Monomer',
fullName: 'My Custom Amino Acid',
naturalAnalogShort: 'A',
atoms: [/* ... */],
bonds: [/* ... */],
attachmentPoints: [/* ... */]
}
};
// Update library (upsert)
await ketcher.updateMonomersLibrary(
JSON.stringify(customMonomers),
{
format: 'ket',
shouldPersist: true // Save to localStorage
}
);
// Or replace entire library
await ketcher.replaceMonomersLibrary(
JSON.stringify(customMonomers),
{
format: 'ket',
shouldPersist: true
}
);// Configure editor settings
ketcher.setSettings({
'general.dearomatize-on-load': false,
'ignoreChiralFlag': true,
'bondThickness': 2,
'disableQueryElements': ['Pol', 'CYH'],
'disableCustomQuery': false
});
// Get current settings
const settings = ketcher.settings;
console.log('Current settings:', settings);// In React component
<Editor
staticResourcesUrl="/public"
structServiceProvider={provider}
customButtons={[
{
id: 'save-to-db',
imageLink: '/icons/database.svg',
title: 'Save to Database'
},
{
id: 'validate',
imageLink: '/icons/check.svg',
title: 'Validate Structure'
}
]}
onInit={(ketcher) => {
ketcher.eventBus.on('CUSTOM_BUTTON_PRESSED', async (buttonId) => {
if (buttonId === 'save-to-db') {
const mol = await ketcher.getMolfile();
await saveToDatabase(mol);
alert('Saved to database!');
}
if (buttonId === 'validate') {
const struct = await ketcher.getKet();
const isValid = validateStructure(struct);
alert(isValid ? 'Valid!' : 'Invalid structure');
}
});
}}
/>// Check if structure contains reaction
const hasReaction = ketcher.containsReaction();
if (hasReaction) {
const rxn = await ketcher.getRxn();
} else {
const mol = await ketcher.getMolfile();
}
// Check if structure has query features
const hasQuery = ketcher.isQueryStructureSelected();
if (hasQuery) {
console.log('Structure contains query features');
const smarts = await ketcher.getSmarts();
}// Set zoom level
ketcher.setZoom(1.5); // 150%
// Minimum and maximum zoom
// ZoomTool.MINZOOMSCALE = 0.2
// ZoomTool.MAXZOOMSCALE = 4.0
// Custom zoom controls
document.getElementById('zoom-in').addEventListener('click', () => {
const currentZoom = ketcher.editor.zoom();
ketcher.setZoom(Math.min(currentZoom * 1.2, 4.0));
});
document.getElementById('zoom-out').addEventListener('click', () => {
const currentZoom = ketcher.editor.zoom();
ketcher.setZoom(Math.max(currentZoom / 1.2, 0.2));
});The ketcher.structService provides low-level access to chemical operations:
interface StructService {
convert(data: ConvertData, options: any): Promise<ConvertResult>
layout(data: LayoutData, options: any): Promise<LayoutResult>
clean(data: CleanData, options: any): Promise<CleanResult>
aromatize(data: AromatizeData, options: any): Promise<AromatizeResult>
dearomatize(data: DearomatizeData, options: any): Promise<DearomatizeResult>
calculateCip(data: CalculateCipData, options: any): Promise<CalculateCipResult>
automap(data: AutomapData, options: any): Promise<AutomapResult>
check(data: CheckData, options: any): Promise<CheckResult>
calculate(data: CalculateData, options: any): Promise<CalculateResult>
recognize(image: Blob, options: any): Promise<RecognizeResult>
generateImageAsBase64(data: string, options: GenerateImageOptions): Promise<string>
getInChIKey(struct: Struct): Promise<string>
toggleExplicitHydrogens(data: any, options: any): Promise<any>
}Note: These methods are low-level and typically used internally. Use the high-level Ketcher API methods when possible.
Most Ketcher methods return Promises and may throw errors:
try {
await ketcher.setMolecule(invalidStructure);
} catch (error) {
console.error('Failed to load structure:', error.message);
}
// In React component
<Editor
structServiceProvider={provider}
staticResourcesUrl="/public"
errorHandler={(message) => {
// Custom error handling
console.error('Ketcher error:', message);
showErrorNotification(message);
}}
/>Common Errors:
- Format conversion errors (invalid SMILES, MOL file syntax)
- Service unavailable (Indigo service down)
- Unsupported operations (reaction methods in macromolecules mode)
- Invalid structure data
Ketcher supports modern browsers:
- Chrome/Edge (latest 2 versions)
- Firefox (latest 2 versions)
- Safari (latest 2 versions)
Requirements:
- ES6+ JavaScript support
- WebAssembly support (for standalone mode)
- SVG rendering
See Contributing Guide.
Please read LICENSE and NOTICE for details.
Copyright (c) 2021 EPAM Systems, Inc.