Visualizing crystal structures in Markdown using 3Dmol.js
Have you ever wanted to embed a crystal structure visualization in your website? Oddly specific question, I know, but recently I found my wanting to do this. As you can see on my homepage, I have finally achieved success but it took a few days to work out the kinks. In order to make sure that no one else endures my pain, here is how you can do this in Markdown using html
and 3Dmol.js.
Caveat: I’ve worked this out for Jekyll but I’m pretty sure the approach is extensible.
Step 1
Take your crystal structure file and convert it into a pdb file. I started out with an xsf but, in principle, you could start out from a [cif][https://www.iucr.org/resources/cif], VASP POSCAR, or any other crystal structure data file, for that matter. If you’re lucky, you might be starting out with a pdf file! You will need to find a software package that converts your file into a pdb. Some packages you might consider are VESTA, Avogadro, and Mercury; all of these are free. If you’re willing to shell out, you could also get CrystalMaker. For reference, I’ve used CrystalMaker and here is the pdb I generated. This structure is an Ag10O7 overlayer that happens to form on the Ag(111) surface in an O2 atmosphere. For more information on this system and the ab initio grand canonical Monte Carlo method I used to find it, check out my recent paper in the Journal of Physical Chemistry C.
Step 2
Now you need to process your pdf file. It turns out that the JavaScript software that we will use, i.e. 3Dmol.js, requires all line breaks to be written as \n
. Instead of doing this by hand, here’s how you can do it on the command line:
awk '{printf "%s\\n", $0}' <path to file> | sed "s/'/\\\'/g"
Hold on to this output, we will need it in a moment.
Step 3
OK, last but not least, now for the html and JavaScript part. First, I’ll present the code and then I’ll go line by line.
<html>
<head>
<script src="/3Dmol-min.js">
</script>
</head>
<div id="container-01" class="mol-container">
</div>
<style>
.mol-container {
width: 60%;
height: 400px;
position: relative;
}
</style>
<script>
$(function() {
let element = $('#container-01');
let config = { backgroundColor : 'white' };
let viewer = $3Dmol.createViewer( element, config );
viewer.addModel("HEADER 5934-refined \nREMARK 1\nREMARK 1 This file was generated by CrystalMaker X for macOS\nREMARK 1 http://crystalmaker.com\nREMARK 1\nCRYST1 11.452 11.452 28.630 120.00 120.00 60.00 P 1\nSCALE1 0.087322 -0.050415 0.035649 0.00000\nSCALE2 -0.000000 0.100830 0.035649 0.00000\nSCALE3 0.000000 0.000000 0.042779 0.00000\nATOM 1 Ag MOL H 0 -0.716 -0.414 1.199 1.00 0.00 Ag0 \nATOM 2 Ag MOL H 0 0.715 2.066 1.199 1.00 0.00 Ag0 \nATOM 3 Ag MOL H 0 2.147 4.545 1.199 1.00 0.00 Ag0 \nATOM 4 Ag MOL H 0 3.578 7.025 1.199 1.00 0.00 Ag0 \nATOM 5 Ag MOL H 0 2.147 -0.414 1.199 1.00 0.00 Ag0 \nATOM 6 Ag MOL H 0 3.578 2.066 1.199 1.00 0.00 Ag0 \nATOM 7 Ag MOL H 0 5.010 4.545 1.200 1.00 0.00 Ag0 \nATOM 8 Ag MOL H 0 6.441 7.024 1.200 1.00 0.00 Ag0 \nATOM 9 Ag MOL H 0 5.010 -0.414 1.199 1.00 0.00 Ag0 \nATOM 10 Ag MOL H 0 6.441 2.066 1.200 1.00 0.00 Ag0 \nATOM 11 Ag MOL H 0 7.873 4.545 1.200 1.00 0.00 Ag0 \nATOM 12 Ag MOL H 0 9.304 7.024 1.200 1.00 0.00 Ag0 \nATOM 13 Ag MOL H 0 7.873 -0.414 1.199 1.00 0.00 Ag0 \nATOM 14 Ag MOL H 0 9.304 2.066 1.200 1.00 0.00 Ag0 \nATOM 15 Ag MOL H 0 10.735 4.546 1.200 1.00 0.00 Ag0 \nATOM 16 Ag MOL H 0 12.167 7.025 1.198 1.00 0.00 Ag0 \nATOM 17 Ag MOL H 0 -2.141 -1.237 3.524 1.00 0.00 Ag0 \nATOM 18 Ag MOL H 0 10.737 1.253 3.505 1.00 0.00 Ag0 \nATOM 19 Ag MOL H 0 12.151 3.730 3.541 1.00 0.00 Ag0 \nATOM 20 Ag MOL H 0 13.597 6.208 3.515 1.00 0.00 Ag0 \nATOM 21 Ag MOL H 0 0.723 -1.242 3.534 1.00 0.00 Ag0 \nATOM 22 Ag MOL H 0 2.148 1.280 3.508 1.00 0.00 Ag0 \nATOM 23 Ag MOL H 0 3.598 3.729 3.539 1.00 0.00 Ag0 \nATOM 24 Ag MOL H 0 5.010 6.225 3.528 1.00 0.00 Ag0 \nATOM 25 Ag MOL H 0 3.568 -1.243 3.534 1.00 0.00 Ag0 \nATOM 26 Ag MOL H 0 5.010 1.253 3.504 1.00 0.00 Ag0 \nATOM 27 Ag MOL H 0 6.446 3.725 3.562 1.00 0.00 Ag0 \nATOM 28 Ag MOL H 0 7.872 6.199 3.529 1.00 0.00 Ag0 \nATOM 29 Ag MOL H 0 6.431 -1.239 3.524 1.00 0.00 Ag0 \nATOM 30 Ag MOL H 0 7.874 1.261 3.508 1.00 0.00 Ag0 \nATOM 31 Ag MOL H 0 9.303 3.724 3.565 1.00 0.00 Ag0 \nATOM 32 Ag MOL H 0 10.734 6.224 3.532 1.00 0.00 Ag0 \nATOM 33 Ag MOL H 0 2.146 7.821 5.863 1.00 0.00 Ag0 \nATOM 34 Ag MOL H 0 -2.108 0.399 5.777 1.00 0.00 Ag0 \nATOM 35 Ag MOL H 0 -0.680 2.905 5.881 1.00 0.00 Ag0 \nATOM 36 Ag MOL H 0 12.149 5.404 5.834 1.00 0.00 Ag0 \nATOM 37 Ag MOL H 0 -0.700 -2.024 5.870 1.00 0.00 Ag0 \nATOM 38 Ag MOL H 0 0.765 0.444 5.779 1.00 0.00 Ag0 \nATOM 39 Ag MOL H 0 2.150 2.947 5.884 1.00 0.00 Ag0 \nATOM 40 Ag MOL H 0 3.593 5.404 5.828 1.00 0.00 Ag0 \nATOM 41 Ag MOL H 0 6.488 6.909 8.311 1.00 0.00 Ag0 \nATOM 42 Ag MOL H 0 3.536 0.443 5.783 1.00 0.00 Ag0 \nATOM 43 Ag MOL H 0 4.978 2.904 5.878 1.00 0.00 Ag0 \nATOM 44 Ag MOL H 0 6.430 5.409 5.833 1.00 0.00 Ag0 \nATOM 45 Ag MOL H 0 4.991 -2.025 5.876 1.00 0.00 Ag0 \nATOM 46 Ag MOL H 0 6.411 0.399 5.779 1.00 0.00 Ag0 \nATOM 47 Ag MOL H 0 7.874 2.993 5.951 1.00 0.00 Ag0 \nATOM 48 Ag MOL H 0 9.287 3.968 8.340 1.00 0.00 Ag0 \nATOM 49 O MOL H 0 2.158 5.468 8.890 1.00 0.00 O0 \nATOM 50 O MOL H 0 -0.673 2.555 8.172 1.00 0.00 O0 \nATOM 51 O MOL H 0 7.873 5.432 8.890 1.00 0.00 O0 \nATOM 52 O MOL H 0 -0.699 -1.560 8.187 1.00 0.00 O0 \nATOM 53 Ag MOL H 0 -0.682 0.497 8.155 1.00 0.00 Ag0 \nATOM 54 Ag MOL H 0 0.779 3.991 8.329 1.00 0.00 Ag0 \nATOM 55 O MOL H 0 5.001 -1.568 8.199 1.00 0.00 O0 \nATOM 56 Ag MOL H 0 9.312 5.411 5.833 1.00 0.00 Ag0 \nATOM 57 O MOL H 0 4.987 2.545 8.172 1.00 0.00 O0 \nATOM 58 Ag MOL H 0 0.758 6.929 8.312 1.00 0.00 Ag0 \nATOM 59 Ag MOL H 0 4.992 0.487 8.157 1.00 0.00 Ag0 \nATOM 60 Ag MOL H 0 6.482 3.954 8.341 1.00 0.00 Ag0 \nATOM 61 Ag MOL H 0 9.259 6.908 8.315 1.00 0.00 Ag0 \nATOM 62 Ag MOL H 0 3.540 3.989 8.324 1.00 0.00 Ag0 \nATOM 63 Ag MOL H 0 3.545 6.935 8.304 1.00 0.00 Ag0 \nATOM 64 Ag MOL H 0 2.146 -2.050 5.892 1.00 0.00 Ag0 \nATOM 65 O MOL H 0 7.878 1.176 7.076 1.00 0.00 O0 \n", "pdb");
viewer.addUnitCell();
viewer.setStyle({}, {sphere : {}});
viewer.render();
});
</script>
</html>
Woah, nelly! It looks like a lot but it really isn’t. Everything is sandwiched between the html tags. Between the head tags is a script tag that contains the path to a minimal implementation of 3Dmol.js, which can be found here. 3Dmol.js contains different functions for reading, configuring, and plotting molecules and crystal structures. The div tag defines a container that will hold the crystal structure and the style tag defines its size. For example, we set the height to be 400 px and the width to be 60% of the that.
Finally, we get to the meat of the script where we pass the structure and define how we would like it plotted. The first and second lines of the function link to the container and set the background color to white, respectively. The third line reads these settings and creates a viewer, our molecular canvas, per say. Now’s the time where we add the structure. The addModel
method has two arguments: (1) a string containing the contents of the crystal structure file and (2) a string identifying what type of file it is. The reason I chose to use pdb files is because pbd was the only file type I recognized that contained information about periodicity. Armed with the output of your awk
command, add it between the two double quotes in the first argument below:
viewer.addModel("", "pdb")
As a final touch, the setStyle
command, unsurprisingly, sets the atom/bond style; here, I have chosen to represent atoms by spheres with radii equal to their vdW radii (for more choices, follow this link). The last line of the code renders the image. I have copied the code below this text and it should produce the same image on my homepage.
Brilliant! There seem to be a ton of other things that you can do with 3Dmol.js. Have fun!