Documents > 2D Shapes

Shapes can be added to 2D Canvases to create complex figures. These may be reactions, or ions or something else. This document covers the different types of 2D shapes and how to use them.

Managing Shapes

2D shapes can be added to 2D Canvases in a couple ways. The first is by adding a single shape by using the Canvas.addShape() function which takes a single shape object as a parameter. Use this function when you want to add to a figure that is already set up. The second method is by using the Canvas.loadContent() function which takes an array of Molecules and an array of Shapes as parameters. Use this when you want to initialize a figure based on a preconfigured set of Molecules and Shapes, such as when reading a reaction file.

You can remove a shape from a 2D Canvas by using the Canvas.removeShape() function which takes the shape to be removed as parameter.

Shapes can be controlled with styles just like the rest of the objects in the ChemDoodle Web Components library. The following styles are global shape styles that will affect all shapes. Specific styles are covered in the individual shape sections below.

  1. shapes_color - The color string to be used to draw shapes.
  2. shapes_lineWidth - The line width used to draw shapes.

Those shapes that also render text can be controlled by these text styles:

  1. text_font_size - Font size.
  2. text_font_families - Font families, this is an array of String objects.
  3. text_font_bold - True if the font is bold.
  4. text_font_italic - True if the font is italic.
  5. text_text_color - Font color.


2D Shape List


A Line is simply a graphical stroke between two points. In addition, lines can be decorated as arrows. The Line constructor takes two Point data structures, the first Point defining where the line begins from and the second Point defining where the line ends. After initialization, you can change the Line.arrowType variable to draw it as an arrow. The following example shows how to do this:

  let viewerLine = new ChemDoodle.ViewerCanvas('viewerLine', 100, 100);
  // create a plain line
  let plainLine = new ChemDoodle.structures.d2.Line(new ChemDoodle.structures.Point(10, 10), new ChemDoodle.structures.Point(90, 10));
  // create a synthetic arrow
  let syntheticArrow = new ChemDoodle.structures.d2.Line(new ChemDoodle.structures.Point(10, 30), new ChemDoodle.structures.Point(90, 30));
  syntheticArrow.arrowType = ChemDoodle.structures.d2.Line.ARROW_SYNTHETIC;
  // create a retrosynthetic arrow
  let retrosyntheticArrow = new ChemDoodle.structures.d2.Line(new ChemDoodle.structures.Point(10, 50), new ChemDoodle.structures.Point(90, 50));
  retrosyntheticArrow.arrowType = ChemDoodle.structures.d2.Line.ARROW_RETROSYNTHETIC;
  // create a resonance arrow
  let resonanceArrow = new ChemDoodle.structures.d2.Line(new ChemDoodle.structures.Point(10, 70), new ChemDoodle.structures.Point(90, 70));
  resonanceArrow.arrowType = ChemDoodle.structures.d2.Line.ARROW_RESONANCE;
  // create an equilibrium arrow
  let equilibriumArrow = new ChemDoodle.structures.d2.Line(new ChemDoodle.structures.Point(10, 90), new ChemDoodle.structures.Point(90, 90));
  equilibriumArrow.arrowType = ChemDoodle.structures.d2.Line.ARROW_EQUILIBRIUM;
  // add the lines
  // repaint

You can also add text above and below lines, such as in reactions:

  let viewerLineText = new ChemDoodle.ViewerCanvas('viewerLineText', 100, 100);
  // create a synthetic arrow
  let syntheticArrow = new ChemDoodle.structures.d2.Line(new ChemDoodle.structures.Point(10, 50), new ChemDoodle.structures.Point(90, 50));
  syntheticArrow.arrowType = ChemDoodle.structures.d2.Line.ARROW_SYNTHETIC;
  syntheticArrow.topText = 'Text on top';
  syntheticArrow.bottomText = 'Text on bottom';
  // repaint

The following styles can be used to modify how lines are drawn:

  1. shapes_arrowLength_2D - the length of the arrowheads drawn.

Atom Mapping

An Atom Mapping defines an association between two atoms, typically that a reactant atom on one side of a reaction is the precursor to the resultant atom in a product molecule on the other side of the reaction. The AtomMapping contructor takes two Atom data structures. ChemDoodle will then place a number tag next two the pair to designate the map. The number in the tag is controlled by the developer, but will automatically be generated in the sketchers. Keep in mind that The following example shows how to do this:

  let viewerMapping = new ChemDoodle.ViewerCanvas('viewerMapping', 100, 100);
  // create two molecules for a map
  let mol1 = new ChemDoodle.structures.Molecule();
  mol1.atoms.push(new ChemDoodle.structures.Atom());
  mol1.atoms.push(new ChemDoodle.structures.Atom('O', 0, -20));
  mol1.bonds.push(new ChemDoodle.structures.Bond(mol1.atoms[0], mol1.atoms[1], 2));
  let mol2 = new ChemDoodle.structures.Molecule();
  mol2.atoms.push(new ChemDoodle.structures.Atom('C', 50, 0));
  mol2.atoms.push(new ChemDoodle.structures.Atom('O', 50, -20));
  mol2.atoms[0].charge = 1;
  mol2.atoms[1].charge = -1;
  mol2.bonds.push(new ChemDoodle.structures.Bond(mol2.atoms[0], mol2.atoms[1], 1));
  // create an arrow between them
  let arrow = new ChemDoodle.structures.d2.Line(new ChemDoodle.structures.Point(10, -10), new ChemDoodle.structures.Point(40, -10));
  arrow.arrowType = ChemDoodle.structures.d2.Line.ARROW_SYNTHETIC;
  // create the atom mapping
  let mapping = new ChemDoodle.structures.d2.AtomMapping(mol1.atoms[1], mol2.atoms[1]);
  // set the map label, if not set, the label will default to '0'
  mapping.label = '1';
  // load into the canvas
  viewerMapping.loadContent([mol1, mol2], [mapping, arrow]);


A Pusher graphically describes the movement of electrons between atoms and bonds. The Pusher contructor takes two chemical data structures (an Atom or a Bond) and the number of electrons to push (you can also input -1 as the number of electrons to specify a Bond Forming pusher). ChemDoodle will then automatically draw a nice bezier arrow from the first chemical object to the second. Keep in mind that Pushers require two chemical objects. The following example shows how to do this:

  let viewerPusher = new ChemDoodle.ViewerCanvas('viewerPusher', 100, 100);
  // create a structure to define the Pusher from
  let mol = new ChemDoodle.structures.Molecule();
  mol.atoms.push(new ChemDoodle.structures.Atom());
  mol.atoms.push(new ChemDoodle.structures.Atom('O', 0, -20));
  mol.bonds.push(new ChemDoodle.structures.Bond(mol.atoms[0], mol.atoms[1], 2));
  // create a 2 electron pusher
  let pusher = new ChemDoodle.structures.d2.Pusher(mol.bonds[0], mol.atoms[1], 2);
  // load into the canvas
  viewerPusher.loadContent([mol], [pusher]);

The following styles can be used to modify how pushers are drawn:

  1. shapes_arrowLength_2D - the length of the arrowheads drawn.


A Bracket graphically annotates a group of objects. The constructor takes two Point data structures; the points define the opposite corners of the rectangle that represents its bounds. You can decorate the bracket with three variables, Bracket.charge, Bracket.mult and Bracket.repeat. The Bracket.charge variable will control the charge amount displayed at the top right of the bracket and is great for polyatomic ions. The Bracket.mult variable will control the multiple amount displayed at the center left of the bracket and is great for reactions and other quanities. The Bracket.repeat variable will control the repeat amount displayed at the bottom right of the bracket and is great for polymers. The following example shows how to do this:

  let viewerBracket = new ChemDoodle.ViewerCanvas('viewerBracket', 100, 100);
  // create a bracket
  let bracket = new ChemDoodle.structures.d2.Bracket(new ChemDoodle.structures.Point(25, 25), new ChemDoodle.structures.Point(75, 75));
  // set the charge amount
  bracket.charge = -2;
  // set the multiple count
  bracket.mult = 12;
  // set the repeat count
  bracket.repeat = 3;
  // load into the canvas
  // repaint

Dynamic Bracket

A Dyanmic Bracket defines a repeating group within a structure. The constructor takes two Bond data structures and ChemDoodle will automatically place and draw the bracket ends on the bonds for you facing in the correct direction. DynamicBrackets will be meaningless when the two bonds are separated in different structures or if the dynamic bracket is part of a complex ring system (single rings are ok). When properly defined, a range will be displayed at the bottom right of the bracket controlled by the n1 and n2 variables. In single rings, a DynamicBracket can be flipped by reversing the order of the bonds into the constructor or by setting the DynamicBracket.flip variable to true. This is a very useful tool for query structures. The following example shows how to initialize a DynamicBracket:

  // inputting a molecule from ChemDoodle JSON, we will be adding the bracket to this molecule
  let input = ChemDoodle.readJSON('{"m":[{"a":[{"x":116,"y":154,"i":"a0"},{"x":133.32050807568876,"y":164,"i":"a1"},{"x":150.64101615137753,"y":154,"i":"a2"},{"x":167.96152422706632,"y":164,"i":"a3"},{"x":185.28203230275508,"y":154,"i":"a4"},{"x":150.6410161513775,"y":134,"i":"a5","l":"O"}],"b":[{"b":0,"e":1,"i":"b0"},{"b":1,"e":2,"i":"b1"},{"b":2,"e":3,"i":"b2"},{"b":3,"e":4,"i":"b3"},{"b":2,"e":5,"i":"b4","o":2}]}]}');
  let m = input.molecules[0];
  let viewerDyanmicBracket = new ChemDoodle.ViewerCanvas('viewerDynamicBracket', 100, 100);
  // create a bracket
  let dynamicbracket = new ChemDoodle.structures.d2.DynamicBracket(m.bonds[0], m.bonds[3]);
  // set the beginning of the repeating group range, default is 1
  dynamicbracket.n1 = 2;
  // set the end of the repeating group range, default is 4
  dynamicbracket.n2 = 3;
  // load into the canvas
  // the dynamic bracket needs to have its contents defined at this point to render correctly, we can make use of a helper function to do so, this requires the _Canvas be sent in
  // repaint the canvas

Variable Attachment Point

A Variable Attachment Point (VAP) defines a substituent that is connected to one or more places on a structure or structures, typically a ring system. The constructor takes the x and y coordinates of the asterisk and ChemDoodle will then render the VAP. A substituent can be defined with a bond order and any number of attachments may be added. Attachment connections are rendered with a dotted line. The following example shows how to initialize a VAP:

  // inputting pyridine from ChemDoodle JSON, we forming the VAP around this molecule
  let input = ChemDoodle.readJSON('{"m":[{"a":[{"x":270,"y":79,"i":"a0","l":"N"},{"x":287.3205080756888,"y":89,"i":"a1"},{"x":287.3205080756888,"y":109,"i":"a2"},{"x":270.00000000000006,"y":119.00000000000004,"i":"a3"},{"x":252.67949192431126,"y":109.0000000000001,"i":"a4"},{"x":252.67949192431118,"y":89.0000000000001,"i":"a5"}],"b":[{"b":0,"e":1,"i":"b0"},{"b":1,"e":2,"i":"b1","o":2},{"b":2,"e":3,"i":"b2"},{"b":3,"e":4,"i":"b3","o":2},{"b":4,"e":5,"i":"b4"},{"b":5,"e":0,"i":"b5","o":2}]}]}');
  let m = input.molecules[0];
  // create the substituent, just a chlorine atom
  let cl = new ChemDoodle.structures.Molecule();
  cl.atoms.push(new ChemDoodle.structures.Atom('Cl', 226, 99));
  // we don't want implicit hydrogens rendered on the Cl atom, so set implicitH to 0
  cl.atoms[0].implicitH = 0;
  // the canvas
  let viewerVAP = new ChemDoodle.ViewerCanvas('viewerVAP', 100, 100);
  // create a VAP, placed at the center of the pyridine ring
  let vap = new ChemDoodle.structures.d2.VAP(270, 99);
  // set the subtituent
  vap.substituent = cl.atoms[0];
  // we can also set the bond order for the substituent, but it is already 1 by default, uncomment the next line to control this
  // vap.bondType = 1;
  // set the attachments, in this case the ortho and para positions in the pyridine
  // load into the canvas
  viewerVAP.loadContent([m, cl], [vap]);

Get your work done with our popular desktop software.