Blog

Signals Blog

WebGL for Chemistry Part 3: Hello, Benzene!

The last article in this series showed how to use WebGL to display a shaded sphere. In this article, we'll be using what we learned to draw a simple 3D representation of benzene.

All Articles in This Series

  1. WebGL for Chemistry Part 1: Introduction
  2. WebGL for Chemistry Part 2: Displaying a Sphere in a View
  3. WebGL for Chemistry Part 3: Hello, Benzene!
  4. WebGL for Chemistry Part 4: Bond, James Bond

The Demo

If you haven't done so already, take a look a the live demo, which I tested on Mac OSX running both Chrome 11.0.696.16 beta and the recently-released Firefox 4.0.

The demo should display something similar to the screenshot above. If you see only a white square, try using the mouse wheel button. I found that on Firefox 4, the demo worked as expected, but on Chrome 11, I needed to use the mouse wheel first. It's not clear why this is - if anyone knows, please get in touch. Also please feel free to post a comment about your experiences getting the demo to work, good or bad.

High-Level Overview of Changes

Looking at the source code for this iteration, you'll notice a number of changes:

Loading a Molfile

We want M3D to be easy to use from the perspective of a Web developer. Doing so requires that we spend some time thinking about what we want the code on the pages that load M3D to look like.

Fortunately, I had already drawn some conclusions about how this might work during the development of ChemWriter, and Spex, two pure-JavaScript chemistry visualization tools.

We can invoke an M3 instance by supplying a div tag to hold the generated content and a script that loads the molfile directly:

<div id="scene"></div>
<script>
  var view = m3d.createView('scene', {
    file: '241\n  -OEChem-03251101473D\n\n 12 12  0     0  0  0  0  0  0999 V2000\n   -1.2131   -0.6884    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n   -1.2028    0.7064    0.0001 C   0  0  0  0  0  0  0  0  0  0  0  0\n   -0.0103   -1.3948    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n    0.0104    1.3948   -0.0001 C   0  0  0  0  0  0  0  0  0  0  0  0\n    1.2028   -0.7063    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n    1.2131    0.6884    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0\n   -2.1577   -1.2244    0.0000 H   0  0  0  0  0  0  0  0  0  0  0  0\n   -2.1393    1.2564    0.0001 H   0  0  0  0  0  0  0  0  0  0  0  0\n   -0.0184   -2.4809   -0.0001 H   0  0  0  0  0  0  0  0  0  0  0  0\n    0.0184    2.4808    0.0000 H   0  0  0  0  0  0  0  0  0  0  0  0\n    2.1394   -1.2563    0.0001 H   0  0  0  0  0  0  0  0  0  0  0  0\n    2.1577    1.2245    0.0000 H   0  0  0  0  0  0  0  0  0  0  0  0\n  1  2  2  0  0  0  0\n  1  3  1  0  0  0  0\n  1  7  1  0  0  0  0\n  2  4  1  0  0  0  0\n  2  8  1  0  0  0  0\n  3  5  2  0  0  0  0\n  3  9  1  0  0  0  0\n  4  6  2  0  0  0  0\n  4 10  1  0  0  0  0\n  5  6  1  0  0  0  0\n  5 11  1  0  0  0  0\n  6 12  1  0  0  0  0\nM  END\n$$$$\n'
  });
</script>
 

The second parameter is an object that holds a property called file, which is nothing more than the contents of 3D molfile downloaded from PubChem. We'll be returning to this parameters object in later iterations as we start to enable customization of M3D's appearance and behavior.

Assembling a View

The createView function brings together the View we created in the previous lesson with a MolfileReader that interprets the atom definitions found in the molfile:

/**
 * @param {string} id
 * @param {object} params hash
 */
m3d.createView = function(id, params) {
  var view = new m3d.View(500, 500);

  view.render(document.getElementById(id));

  var reader = new m3d.MolfileReader();
  var molecule = reader.read(params.file);

  for (var i = 0; i < molecule.atoms.length; i++) {
    view.drawAtom(molecule.atoms[i]);
  }

  view.redraw();

  return view;
 

There's really not much to this function. It merely instantiates and renders a View, reads the molfile, and draws each atom encoded in it.

Reading the Molfile

A very, very simple MolfileReader has been implemented based loosely on the molfile reader found in TwirlyMol by Noel O'Boyle:

/**
 * @param {string} molfile
 * @return {Object}
 */
m3d.MolfileReader.prototype.read = function(molfile) {
  var lines = molfile.split("\n");
  var atomCount = parseFloat(lines[3].substring(0, 3));
  var atoms = [];

  for (var i = 0; i < atomCount; i++) {
    atoms.push({
      x: parseFloat(lines[i + 4].substring(0, 10)),
      y: parseFloat(lines[i + 4].substring(10, 20)),
      z: parseFloat(lines[i + 4].substring(20, 30))
    });
  }

  return { atoms: atoms };
};
 

Clearly this is just a start. Future iterations will flesh out this reader as we begin to incorporate more features from molfiles.

Drawing Atoms

And now it's time to turn our attention to the only piece of the code that actually touches WebGL. In this iteration, we've opted for a very basic implementation of View#drawAtom in which all atoms get the same size and color:

/**
 * @param {object} atom The atom to draw
 */
m3d.View.prototype.drawAtom = function(atom) {
  var sphere = new PhiloGL.O3D.Sphere({ nlat: 20, nlong: 20, radius: 0.5, colors: [1, 0, 0, 1] });

  sphere.position.set(atom.x, atom.y, atom.z);
  sphere.update();

  this.scene_.add(sphere);
};
 

Notice the call to the PhiloGL.O3D.Sphere constructor. Recall that we're using PhiloGL as a high-level interface to WebGL. One of the reasons for choosing PhiloGL is that it offers a good basic set of drawing primitives, such as Sphere that we'll need to depict molecules in 3D.

We'll be coming back to the drawAtom function in later iterations as we incorporate size and color information into the display.

Conclusions

In this iteration, we can now read standard molfiles and represent the atoms as uniform, shaded spheres. Our next iteration will take up the subject of linking these atoms with bonds.