Introduction and Background
ChemDoodle Web Components are pure javascript objects derived from ChemDoodle™ to solve common chemistry related tasks on the web. These components are powerful, fully customizable, easy to implement, and are free under the open source GPL license (this does not mean that your website needs to be GPL). They leverage the technology of the HTML 5 specification’s Canvas element. The Canvas element allows web developers to dynamically draw 2D vector graphics in a web page without the use of 3rd party plugins, such as Flash or Silverlight. The HTML 5 specification has not been completely adopted by all browsers yet, but it should be fully implemented on most browsers soon. On Mac OSX, Apple Safari 4+, Mozilla Firefox 3.5+and Google Chromium all support Canvas beautifully. By utilizing this powerful element, we were able to develop a software development kit, complete with graphical user interface components, completely in javascript. By using javascript to create scientific applications, implementation is simplified, extensibility is improved, security issues are handled by the browser and the most widespread programming language in the world is now more accessible to scientists.
The ChemDoodle Web Components currently provide 6 extendable graphical components as well as a growing cheminformatics library:
- Viewer– view a static styled drawing
- Rotator– provide a rotating animation of structures
- Transformer– rotate, scale and translate structures
- Doodler– draw structures with an interface that mimics ChemDoodle
- MolGrabber– provide access to databases such as PubChem right on your website
- File Loader– allow users to load their own molecule files straight to your web based algorithms
3D Graphics and OpenGL ES
The Canvas element, as currently implemented, allows for only 2D graphics. That is all about to change. Only a couple days ago, WebGL (complete with a very impressive video!) was quietly introduced into the nightly build of WebKit. Some readers may already be very familiar with WebKit, as it is the open source browser engine that runs Safari. WebGL opens new possibilities for the Canvas element, and I quote the best explanation I’ve seen so far, “The goal of WebGL is to expose the low-level OpenGL ES 2.0 APIs through JavaScript so that they can be used to draw hardware-accelerated 3D graphics in the HTML Canvas element” (source). We will soon be able to develop fully native web applications with 2D and 3D graphical user interfaces completely in javascript for web browsers. Imagine advanced quantum computations with beautiful output, wide-spread open source simulation packages, and functional molecular modelers all optimized for the web and easily integrated into Web 3.0 sites! We will be developing 3D ChemDoodle Web Components as soon as WebGL is adopted by the browsers, and we aim to provide the funding, development and support for this open source package that the community requires.
While OpenGL ES (OpenGL for Embedded Systems) is only a subset of OpenGL, it has proven to be quite effective in producing great 3D molecular graphics on non-desktop platforms. Brad Larson at Sunset Lake Software mastered the technology to create the impressive Molecules app on the iPhone. Expect this technology to allow for quick and engrossing graphics without the need for fully installed applications on the desktop.
Future Expectations
Rich Apodaca continues to be at the forefront of emerging web technologies and provides insight on their impact to the sciences, including javascript/Canvas, on his blog Depth-First. Over the past year, they have seen the Canvas technology develop and have investigated many of its shortcomings. The community continues to push for more robust Canvas features and there has been significant progress and the technology is entirely capable of supporting 2D graphical scientific applications.
One issue is with competing technologies, such as applets, other multimedia plugins and those technologies not yet developed. While it is true that the internet is a continuously morphing environment, this author is confident that Canvas technology will be running the websites of the future. The reasoning is that it is a native browser technology, and users will be likely to choose it over cumbersome 3rd party plugins. Take CSS for instance, if website publishers are given a choice between using CSS or some other 3rd party plugin to organize their HTML pages, the resounding answer would be CSS. Additionally, web site visitors don’t want to wait for applets to load and developers don’t want the hassles associated with them. In order to compete in the future, developers will have to create engrossing Web 3.0 sites, and Canvas is the answer.
In another interesting twist a few months ago, Google Chrome OS was announced. It is an interesting proposal, assuming users that spend most of their time on the web really only need a web browser on their computer. The conclusion is that a huge part of the market will benefit from a browser based OS that has minimal loading times and takes users straight to the web. For scientists, this seems a little far-fetched, as the very complex applications we have become reliant on are desktop applications. Porting them to javascript or even beginning to solve such a transition seems very unlikely. This is compounded by the difficulty inherent with creating javascript applications: debugging is incredibly tedious and there isn’t a good IDE in sight. However, javascript is the most distributed runtime on the planet and we can be sure there will be several situations where scientists will require just web applications that are continuously updated and easily distributed. Certainly, if Google Chrome OS takes over, we will have a basis to continue to create great software.
Walkthrough for a Demo PDB Web Application
Now that I have provided some background on the technology, I will be demonstrating how to write a web application using the ChemDoodle Web Components. The application has a simple goal: visitors search the PDB using structure codes and then view an animation of the result, while the most recently viewed structures are saved in a graphical history. The finished product can be seen here. Use the browser’s View Source function to see the source in its entirety. Note that previously Canvas referred to the HTML 5 element. Canvas from now on will refer to the base ChemDoodle Web Component class of the same name.
To begin, I always sketch a layout of how the application appears in my mind. I pictured a simple layout, with the history to the left and search form at the top-left corner.
Conveniently, the use of the ChemDoodle Web Components allows me to produce any layout with any functionality I wish. Relying on applets may confine us to using a predefined interface. Since I want the left and right sections to align horizontally, I’ll use a HTML table for simplicity. The table will have 1 row and 3 columns, 1 for an image used to provide explanation for the empty history components, 1 for the history and search form, and 1 for the animation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<table> <tr> <td> <!--image to provide some explanation of the history components--> <img src="/images/pdbCDWHistory.png" /> </td> <td> <!--the search form on top, with the history components below--> </td> <td> <!--the large rotation animation--> </td> </tr> </table> |
The first cell contains a single image. The second cell is the most complex, so I discuss it last. In the third cell I place a ChemDoodle Web Component RotatorCanvas that will display an animation of the main structure rotating about the y-axis. The file2js PHP script is provided with the ChemDoodle Web Components, and is an example of how to pass molecular data to the javascript components.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<script> // construct the rotator component with 600x600px dimensions; // this variable is actually defined before the search form to ensure its existence // during page compilation rotate3D = new RotatorCanvas('rotate3D', 600, 600, true); // call the yet to be written setupVisuals() function to standardize the style // for all components setupVisuals(rotate3D); // rotate around the y-axis rotate3D.yIncrement = -Math.PI / 360; rotate3D.xIncrement = 0; rotate3D.zIncrement = 0; // read in the PDB file and turn off ring perception (to improve efficiency, rings // aren't necessary for this app) var mol = readPDB(<?php file2js('./molecules/1BNA.pdb'); ?>); mol.findRings = false; // load the molecule into the component rotate3D.loadMolecule(mol); // begin rotation rotate3D.startRotation(); </script> |
The second cell has two types of components, the HTML form for PDB code input, and the user’s most recent viewing history. The HTML form is a little complex as a trick is used to pass the PDB file to the javascript functions. Due to security issues, javascript cannot access local or server files, so a server side script accesses the data instead, as defined in the sample PHP file CDWProteinDataBank.php. This is an asynchronous call, and when finished, the PHP script loads the file into the hidden iframe below, which onload, calls the javascript function GetPdbFromFrame(). The CDWProteinDataBank.php file is provided with the ChemDoodle Web Components and should be used as an example only. You should write your own server side scripts to appropriately suit your own website’s security and logistic concerns.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<center> <!--generate the HTML form that calls the server side CDWProteinDataBank script to retrieve the input structure. This is an asynchronous call, and when finished, the PHP script loads the file into the hidden iframe below, which onload, calls the javascript function GetPdbFromFrame(), which has not yet been defined. The ValidateMolecule() function ensures that a valid string was input into the text field--> <form name="PDBForm" method="POST" action="/CDWProteinDataBank.php" target="HiddenPDBFrame" onSubmit="ValidateMolecule(PDBForm); return false;"> <!--text field--> <input type="text" name="q" value="" size="4" maxlength="4" /> <!--submit button that displays 'Retrieve'--> <input type="submit" name="submitbutton" value="Retrieve" /> </form> <!—this hidden iframe holds the PDB file to be loaded by the javascript component, as javascript cannot directly access files for security reasons. Look at CDWProteinDataBank.php and the GetPdbFromFrame() function in this demo to see exactly how this works--> <iframe id="HMGF-rotate3D" name="HiddenPDBFrame" height="0" width="0" style="display:none;" onLoad="GetPdbFromFrame('HMGF-rotate3D', rotate3D)"> </iframe> </center> |
The user’s viewing history will be placed below the search form. I would like this to be a graphical history, so I want the images of previous structures to be shown here. In addition, clicking the structure should transfer it to the main rotator and hovering should highlight it for visual feedback. The possibilities are endless, and I will extend the ChemDoodle Web Components’ Canvas base class to provide this functionality after this section. I call the class HistoryCanvas, and very simply, I create four of them with a dimension that allows for horizontal alignment with the rotator canvas, and stack them on top of each other. I have now laid out all the components on the HTML page.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<script> // initialize a history component to 140x140px, which fits well; // the HistoryCanvas class is one we will extend from the ChemDoodle Web Components // to match the intended functionality var mem1 = new HistoryCanvas('mem1', 140, 140, true); // call the same setupVisuals() function as the rotator to standardize the graphics setupVisuals(mem1); // load a placeholder molecule into the first history component to draw attention var mol = readPDB( <?php file2js('./molecules/1TRZ.pdb'); ?>); mol.findRings = false; mem1.loadMolecule(mol); </script> <br> <script> // repeat for 3 more history components var mem2 = new HistoryCanvas('mem2', 140, 140, true); setupVisuals(mem2); mem2.repaint(); </script> <br> <script> var mem3 = new HistoryCanvas('mem3', 140, 140, true); setupVisuals(mem3); mem3.repaint(); </script> <br> <script> var mem4 = new HistoryCanvas('mem4', 140, 140, true); setupVisuals(mem4); mem4.repaint(); </script> |
After the components have been defined, the rest of the functions need to be written. I’ll begin with the most important function, which is actually a class. In javascript, all objects are functions and functions are objects. So we create the class by creating a function and declaring member methods and parameters. The history components to be added should display the structure, and should also be clickable. Once clicked, the saved molecule loads into the main rotator. Also, hovering over a history component should highlight it, like a hyperlink. The class is aptly named HistoryCanvas. In javascript, to create a subclass, I define the prototype of the child class. The prototype of the HistoryCanvas class is set to the ChemDoodle Web Components’ Canvas class to absorb its graphical functionality.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
<script> // the HistoryCanvas class, takes the HTML id, width and height dimensions as parameters function HistoryCanvas(id, width, height) { // the static all Array contains all the components to receive // mouse and keyboard events from the Canvas user input manager all[all.length] = this; // call the super's create() method to create the component and lay it out on // the HTML page this.create(id, width, height); // save the last mouse event point this.p = null; // override the Canvas abstract drawChildExtras() function, it is an optional // override, and does nothing if not overrode; // this function will draw extra graphics after the molecule has been drawn, a // green highlight will be drawn around the border of the component if the last // mouse event point is in the component this.drawChildExtras = function(ctx) { if (this.p != null) { ctx.strokeStyle = '#ADFF75'; ctx.lineWidth = '4'; ctx.strokeRect(0, 0, this.width, this.height); } } // another optional override, this receives the mouseup event from the Canvas // input manager; // when clicked, the component will place a copy of its current contents into the // main rotator after the molecule from the rotator has been pushed into the user's // viewing history this.mouseup = function(p) { if (p.x > 0 && p.y > 0 && p.x < this.width && p.y < this.height) { var save = copy(this.molecule); save.findRings = false; cycleMolecules(); rotate3D.loadMolecule(save); } } // another optional override, this receives the mouseexit event from the Canvas // input manager; // clears the highlight this.mouseexit = function(p) { if (this.p != null) { this.p = null; this.repaint(); } } // another optional override, this receives the mouse move event from the Canvas // input manager // shows the highlight if necessary this.move = function(p) { if (this.p == null && p != null) { this.p = p; this.repaint(); } } return true; } // the HistoryCanvas class extends the ChemDoodle Web Components' base Canvas class HistoryCanvas.prototype = new Canvas(); </script> |
Now that the HistoryCanvas class has been defined, there are three functions left to be completed: setupVisuals(), GetPdbFromFrame(), and cycleMolecules(). The first, setupVisuals(), just sets the visual specifications of an input component so that all created components can be standardized by one function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<code> <script> // this function sets up the visual specifications for the components, a single method // is used to standardize the graphics function setupVisuals(canvas) { // use JMol colors for bonds, this will gradient between the two constituent atoms canvas.specs.bonds_useJMOLColors = true; // thicken the bonds canvas.specs.bonds_width = 3; // turn on this setting for better contrast between overlapping bonds canvas.specs.bonds_clearOverlaps = true; // turn off atoms, so we get a wireframe like model canvas.specs.atoms_display = false; // set the background to black for a classic modeling look canvas.specs.backgroundColor = 'black'; } </script> |
The second method, GetPdbFromFrame(), is the function to be called after the PDB file has been retrieved and inserted into the hidden frame. The function retrieves the file, parses it, pushes the currently rotating structure into the user’s viewing history and loads the new structure.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<script> // this function is called when the hidden iframe is filled with a PDB file; // this function will read the PDB file from the hidden iframe, parse it, and load // it into the rotator function GetPdbFromFrame(frameId, canvas) { // if canvas (input component) is null, return, this should never be the case if (!canvas) { return; } // get the PDB file from the hidden frame var mol = document.getElementById(frameId).contentDocument.body.innerHTML; // if the server encountered an error, this will be output, can be changed in // CDWProteinDataBank.php if (mol.match('^ChemDoodle Web Components Query Error.')) { alert(mol); } else { // stop the main rotation during the load canvas.stopRotation(); // push the current structure in the rotator down into the user's viewing // history cycleMolecules(); // parse and create the molecule var m = readPDB(mol); m.findRings = false; // load the molecule into the rotator canvas.loadMolecule(m); // reanimate the component canvas.startRotation(); } } </script> |
The last function, cycleMolecules(), pushes the currently animating structure into the user’s viewing history. The scale attribute is transferred as well as the structure so it doesn’t need to be recalculated. The top history component must load the molecule from the rotate3D object to make sure to fit it properly, since the component sizes are different.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<script> // this simple function just pushes the molecules down through history // note that the molecule, as well as the scale is passed, so no recalculations // are required mem1 must load the molecule from the rotate3D object to make sure // to fit it properly function cycleMolecules() { if (mem3.molecule != null) { mem4.molecule = mem3.molecule; mem4.specs.scale = mem3.specs.scale; mem4.repaint(); } if (mem2.molecule != null) { mem3.molecule = mem2.molecule; mem3.specs.scale = mem2.specs.scale; mem3.repaint(); } if (mem1.molecule != null) { mem2.molecule = mem1.molecule; mem2.specs.scale = mem1.specs.scale; mem2.repaint(); } if (rotate3D.molecule != null) { mem1.loadMolecule(rotate3D.molecule); } } </script> |
And that finishes the project. Load it up to see the web app in action!
Closing Statement
The ChemDoodle Web Components provide scientists with a javascript cheminformatics library and GUI toolkit for quickly producing web applications using the HTML 5 specification’s Canvas element. With only a few functions, an entire application for querying the PDB and displaying nice animations was built. The future of these technologies is very promising and iChemLabs aims to support this open source project to significantly benefit the scientific community. For any questions, comments or requests, please email the author at kevin@ichemlabs.com.
One thought on “ChemDoodle Web Components”
Comments are closed.