diff --git a/README.md b/README.md
index 29881c843b2f5e4d025d67af3d1129c341940fa1..bea25cde6c5ecf65b2286159027280754b09413c 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,8 @@ The goal of **Mol\*** (*/'mol-star/*) is to provide a technology stack that will
 
 This particular project is the implementation of this technology (still under development).
 
+*If you are looking for the "MOLeculAR structure annoTator", that package is now available on NPM as [MolArt](https://www.npmjs.com/package/molart).*
+
 ## Project Overview
 
 The core of Mol* currently consists of these modules (see under `src/`):
@@ -61,8 +63,7 @@ This project builds on experience from previous solutions:
     DEBUG=molstar npm run watch
 
 ### Build for production:
-    npm run build
-    NODE_ENV=production npm run build-webpack
+    NODE_ENV=production npm run build
 
 **Run**
 
@@ -94,7 +95,7 @@ Install CIFTools `npm install ciftools -g`
 ### Other scripts
 **Create chem comp bond table**
 
-    export NODE_PATH="lib"; node --max-old-space-size=8192 build/src/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b
+    export NODE_PATH="lib"; node --max-old-space-size=4096 lib/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b
 
 **Test model server**
 
@@ -132,14 +133,17 @@ To get syntax highlighting for shader and graphql files add the following to Vis
 
 ## Publish
 
-## Prerelease
+### Prerelease
     npm version prerelease # asumes the current version ends with '-dev.X'
     npm publish --tag next
 
-## Release
+### Release
     npm version 0.X.0 # provide valid semver string
     npm publish
 
+## Deploy
+    node ./scripts/deploy.js # currently updates the viewer on molstar.org/viewer
+
 ## Contributing
 Just open an issue or make a pull request. All contributions are welcome.
 
diff --git a/package.json b/package.json
index d1f18d63a1792e98a6fdf73ab0824c7585e8d719..3c72052a1fe35f1638fae0ee923a33acdda51e80 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "molstar",
-  "version": "0.2.1",
+  "version": "0.2.2",
   "description": "A comprehensive macromolecular library.",
   "homepage": "https://github.com/molstar/molstar#readme",
   "repository": {
@@ -23,7 +23,6 @@
     "watch-webpack": "webpack -w --mode development --display minimal",
     "model-server": "node lib/servers/model/server.js",
     "model-server-watch": "nodemon --watch lib lib/servers/model/server.js",
-    "postinstall": "npm run build",
     "preversion": "npm run test",
     "postversion": "git push && git push --tags",
     "prepublishOnly": "npm run test && npm run build"
@@ -66,9 +65,10 @@
     "circular-dependency-plugin": "^5.0.2",
     "concurrently": "^4.1.0",
     "cpx": "^1.5.0",
-    "css-loader": "^2.1.1",
+    "css-loader": "^3.0.0",
     "extra-watch-webpack-plugin": "^1.0.3",
-    "file-loader": "^3.0.1",
+    "file-loader": "^4.0.0",
+    "fs-extra": "^8.0.1",
     "graphql-code-generator": "^0.18.2",
     "graphql-codegen-time": "^0.18.2",
     "graphql-codegen-typescript-template": "^0.18.2",
@@ -76,26 +76,25 @@
     "jest-raw-loader": "^1.0.1",
     "mini-css-extract-plugin": "^0.7.0",
     "node-sass": "^4.12.0",
-    "raw-loader": "^2.0.0",
+    "raw-loader": "^3.0.0",
     "resolve-url-loader": "^3.1.0",
     "sass-loader": "^7.1.0",
+    "simple-git": "^1.113.0",
     "style-loader": "^0.23.1",
     "ts-jest": "^24.0.2",
     "tslint": "^5.17.0",
     "typescript": "^3.5.1",
-    "uglify-js": "^3.6.0",
-    "util.promisify": "^1.0.0",
-    "webpack": "^4.32.2",
-    "webpack-cli": "^3.3.2"
+    "webpack": "^4.33.0",
+    "webpack-cli": "^3.3.4"
   },
   "dependencies": {
     "@types/argparse": "^1.0.36",
     "@types/benchmark": "^1.0.31",
     "@types/compression": "0.0.36",
-    "@types/express": "^4.16.1",
+    "@types/express": "^4.17.0",
     "@types/jest": "^24.0.13",
-    "@types/node": "^12.0.4",
-    "@types/node-fetch": "^2.3.4",
+    "@types/node": "^12.0.8",
+    "@types/node-fetch": "^2.3.5",
     "@types/react": "^16.8.19",
     "@types/react-dom": "^16.8.4",
     "@types/swagger-ui-dist": "3.0.0",
@@ -109,7 +108,8 @@
     "react": "^16.8.6",
     "react-dom": "^16.8.6",
     "rxjs": "^6.5.2",
-    "swagger-ui-dist": "^3.22.2",
+    "swagger-ui-dist": "^3.22.3",
+    "util.promisify": "^1.0.0",
     "xhr2": "^0.2.0"
   }
 }
diff --git a/scripts/deploy.js b/scripts/deploy.js
new file mode 100644
index 0000000000000000000000000000000000000000..c1d4fc30fdb5d60f0b9161ff7294b7926e975029
--- /dev/null
+++ b/scripts/deploy.js
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+const git = require('simple-git')
+const path = require('path')
+const fs = require("fs")
+const fse = require("fs-extra")
+
+const remoteUrl = "https://github.com/molstar/molstar.github.io.git"
+const buildDir = path.resolve(__dirname, '../build/')
+const deployDir = path.resolve(buildDir, 'deploy/')
+const localPath = path.resolve(deployDir, 'molstar.github.io/')
+
+function log(command, stdout, stderr) {
+    if (command) {
+        console.log('\n###', command)
+        stdout.pipe(process.stdout)
+        stderr.pipe(process.stderr)
+    }
+}
+
+function copyViewer() {
+    console.log('\n###', 'copy viewer files')
+    const viewerBuildPath = path.resolve(buildDir, '../build/viewer/')
+    const viewerDeployPath = path.resolve(localPath, 'viewer/')
+    fse.copySync(viewerBuildPath, viewerDeployPath, { overwrite: true })
+}
+
+if (!fs.existsSync(localPath)) {
+    console.log('\n###', 'create localPath')
+    fs.mkdirSync(localPath, { recursive: true })
+}
+
+process.chdir(localPath);
+
+if (!fs.existsSync(path.resolve(localPath, '.git/'))) {
+    console.log('\n###', 'clone repository')
+    git()
+        .outputHandler(log)
+        .clone(remoteUrl, localPath)
+        .fetch(['--all'])
+        .exec(copyViewer)
+        .add(['-A'])
+        .commit('updated viewer')
+        .push()
+} else {
+    console.log('\n###', 'update repository')
+    git()
+        .outputHandler(log)
+        .fetch(['--all'])
+        .reset(['--hard', 'origin/master'])
+        .exec(copyViewer)
+        .add(['-A'])
+        .commit('updated viewer')
+        .push()
+}
\ No newline at end of file
diff --git a/src/apps/chem-comp-bond/create-table.ts b/src/apps/chem-comp-bond/create-table.ts
index cc29af24a60f6490751d22ec1b4101d68cb3665e..ba541008609cb3c9c61c6dd09ff192f8e3535ad9 100644
--- a/src/apps/chem-comp-bond/create-table.ts
+++ b/src/apps/chem-comp-bond/create-table.ts
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -15,12 +15,13 @@ const readFile = util.promisify(fs.readFile)
 const writeFile = util.promisify(fs.writeFile)
 
 import { Progress } from '../../mol-task'
-import { Database, Table, DatabaseCollection, Column } from '../../mol-data/db'
+import { Database, Table, DatabaseCollection } from '../../mol-data/db'
 import { CIF } from '../../mol-io/reader/cif'
 import { CifWriter } from '../../mol-io/writer/cif'
 import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd'
 import { SetUtils } from '../../mol-util/set'
 import { DefaultMap } from '../../mol-util/map'
+import { mmCIF_chemCompBond_schema } from '../../mol-io/reader/cif/schema/mmcif-extras';
 
 export async function ensureAvailable(path: string, url: string) {
     if (FORCE_DOWNLOAD || !fs.existsSync(path)) {
@@ -74,16 +75,6 @@ export function getEncodedCif(name: string, database: Database<Database.Schema>,
 type CCB = Table<CCD_Schema['chem_comp_bond']>
 type CCA = Table<CCD_Schema['chem_comp_atom']>
 
-const ChemCompBond_Schema = {
-    comp_id: CCD_Schema['chem_comp_bond'].comp_id,
-    atom_id_1: CCD_Schema['chem_comp_bond'].atom_id_1,
-    atom_id_2: CCD_Schema['chem_comp_bond'].atom_id_2,
-    value_order: CCD_Schema['chem_comp_bond'].value_order,
-    pdbx_aromatic_flag: CCD_Schema['chem_comp_bond'].pdbx_aromatic_flag,
-    pdbx_stereo_config: CCD_Schema['chem_comp_bond'].pdbx_stereo_config,
-    molstar_protonation_variant: Column.Schema.Str()
-}
-
 function ccbKey(compId: string, atomId1: string, atomId2: string) {
     return atomId1 < atomId2 ? `${compId}:${atomId1}-${atomId2}` : `${compId}:${atomId2}-${atomId1}`
 }
@@ -202,14 +193,14 @@ async function createBonds() {
         }
     }
 
-    const bondTable = Table.ofArrays(ChemCompBond_Schema, {
+    const bondTable = Table.ofArrays(mmCIF_chemCompBond_schema, {
         comp_id, atom_id_1, atom_id_2, value_order,
         pdbx_aromatic_flag, pdbx_stereo_config, molstar_protonation_variant
     })
 
     const bondDatabase =  Database.ofTables(
         TABLE_NAME,
-        { chem_comp_bond: ChemCompBond_Schema },
+        { chem_comp_bond: mmCIF_chemCompBond_schema },
         { chem_comp_bond: bondTable }
     )
 
@@ -220,12 +211,15 @@ async function run(out: string, binary = false) {
     const bonds = await createBonds()
 
     const cif = getEncodedCif(TABLE_NAME, bonds, binary)
+    if (!fs.existsSync(path.dirname(out))) {
+        fs.mkdirSync(path.dirname(out));
+    }
     writeFile(out, cif)
 }
 
 const TABLE_NAME = 'CHEM_COMP_BONDS'
 
-const DATA_DIR = path.join(__dirname, '..', '..', '..', 'data')
+const DATA_DIR = path.join(__dirname, '..', '..', '..', 'build/data')
 const CCD_PATH = path.join(DATA_DIR, 'components.cif')
 const PVCD_PATH = path.join(DATA_DIR, 'aa-variants-v1.cif')
 const CCD_URL = 'http://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif'
diff --git a/src/examples/proteopedia-wrapper/helpers.ts b/src/examples/proteopedia-wrapper/helpers.ts
index a15bcca86c8d9c3d20d16417680c583f3cc5f4a1..7d1360c3e31e095ea745b9afb91d16e54cb21131 100644
--- a/src/examples/proteopedia-wrapper/helpers.ts
+++ b/src/examples/proteopedia-wrapper/helpers.ts
@@ -105,6 +105,8 @@ export enum StateElements {
     ModelProps = 'model-props',
     Assembly = 'assembly',
 
+    VolumeStreaming = 'volume-streaming',
+
     Sequence = 'sequence',
     SequenceVisual = 'sequence-visual',
     Het = 'het',
diff --git a/src/examples/proteopedia-wrapper/index.html b/src/examples/proteopedia-wrapper/index.html
index 288db037a5d4f2406c2a3b486fd2417dde62e592..5fd7d7bbc083c8f9e1637603fd524e2021e13a8d 100644
--- a/src/examples/proteopedia-wrapper/index.html
+++ b/src/examples/proteopedia-wrapper/index.html
@@ -40,6 +40,13 @@
                 width: 100%;
                 display: block;
             }
+
+            #volume-streaming-wrapper {
+                position: absolute;
+                top: 100px;
+                left: 780px;
+                width: 300px;
+            }
         </style>
         <link rel="stylesheet" type="text/css" href="app.css" />
         <script type="text/javascript" src="./index.js"></script>
@@ -55,6 +62,7 @@
             </select>
         </div>
         <div id="app"></div>
+        <div id="volume-streaming-wrapper"></div>
         <script>
             // it might be a good idea to define these colors in a separate script file 
             var CustomColors = [0x00ff00, 0x0000ff];
@@ -66,7 +74,7 @@
 
             function $(id) { return document.getElementById(id); }
         
-            var pdbId = '1eve', assemblyId= 'preferred';
+            var pdbId = '1cbs', assemblyId= 'preferred';
             var url = 'https://www.ebi.ac.uk/pdbe/static/entry/' + pdbId + '_updated.cif';
             var format = 'cif';
             
@@ -151,6 +159,11 @@
             addHetGroupsContainer();
 
             addSeparator();
+            addHeader('Exp. Data');
+            addControl('Init', () => PluginWrapper.experimentalData.init($('volume-streaming-wrapper')));
+            addControl('Remove', () => PluginWrapper.experimentalData.remove());
+
+            addSeparator(); 
             addHeader('State');
 
             var snapshot;
diff --git a/src/examples/proteopedia-wrapper/index.ts b/src/examples/proteopedia-wrapper/index.ts
index bd9d4447f5a85563525caff904748792d1fdaa98..d444fed9d4c138a0488ff63cb1d0a8cd19dc1b15 100644
--- a/src/examples/proteopedia-wrapper/index.ts
+++ b/src/examples/proteopedia-wrapper/index.ts
@@ -4,6 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
+import * as ReactDOM from 'react-dom';
 import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
 import './index.html'
 import { PluginContext } from '../../mol-plugin/context';
@@ -13,11 +14,11 @@ import { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transfo
 import { Color } from '../../mol-util/color';
 import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin/state/objects';
 import { AnimateModelIndex } from '../../mol-plugin/state/animation/built-in';
-import { StateBuilder, StateObject } from '../../mol-state';
+import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
 import { EvolutionaryConservation } from './annotation';
 import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo, StateElements } from './helpers';
 import { RxEventHelper } from '../../mol-util/rx-event-helper';
-import { ControlsWrapper } from './ui/controls';
+import { ControlsWrapper, volumeStreamingControls } from './ui/controls';
 import { PluginState } from '../../mol-plugin/state';
 import { Scheduler } from '../../mol-task';
 import { createProteopediaCustomTheme } from './coloring';
@@ -26,6 +27,8 @@ import { BuiltInStructureRepresentations } from '../../mol-repr/structure/regist
 import { BuiltInColorThemes } from '../../mol-theme/color';
 import { BuiltInSizeThemes } from '../../mol-theme/size';
 import { ColorNames } from '../../mol-util/color/tables';
+import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
+import { ParamDefinition } from '../../mol-util/param-definition';
 // import { Vec3 } from 'mol-math/linear-algebra';
 // import { ParamDefinition } from 'mol-util/param-definition';
 // import { Text } from 'mol-geo/geometry/text/text';
@@ -264,6 +267,28 @@ class MolStarProteopediaWrapper {
         }
     }
 
+    private experimentalDataElement?: Element = void 0;
+    experimentalData = {
+        init: async (parent: Element) => {
+            const asm = this.state.select(StateElements.Assembly)[0].obj!;
+            const params = ParamDefinition.getDefaultValues(InitVolumeStreaming.definition.params!(asm, this.plugin));
+            params.behaviorRef = StateElements.VolumeStreaming;
+            params.defaultView = 'box';
+            await this.plugin.runTask(this.state.applyAction(InitVolumeStreaming, params, StateElements.Assembly));
+            this.experimentalDataElement = parent;
+            volumeStreamingControls(this.plugin, parent);
+        },
+        remove: () => {
+            const r = this.state.select(StateSelection.Generators.ofTransformer(CreateVolumeStreamingInfo))[0];
+            if (!r) return;
+            PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.state, ref: r.transform.ref });
+            if (this.experimentalDataElement) {
+                ReactDOM.unmountComponentAtNode(this.experimentalDataElement);
+                this.experimentalDataElement = void 0;
+            }
+        }
+    }
+
     hetGroups = {
         reset: () => {
             const update = this.state.build().delete(StateElements.HetGroupFocus);
diff --git a/src/examples/proteopedia-wrapper/ui/controls.tsx b/src/examples/proteopedia-wrapper/ui/controls.tsx
index 3de24ed90c969ad5902bea4443149ef48901e9a2..df7938cc3c9b1fa92fc1b9ddf2bc4d74c769fe79 100644
--- a/src/examples/proteopedia-wrapper/ui/controls.tsx
+++ b/src/examples/proteopedia-wrapper/ui/controls.tsx
@@ -5,10 +5,14 @@
  */
 
 import * as React from 'react';
+import * as ReactDOM from 'react-dom';
 import { PluginUIComponent } from '../../../mol-plugin/ui/base';
-import { CurrentObject } from '../../../mol-plugin/ui/plugin';
+import { CurrentObject, PluginContextContainer } from '../../../mol-plugin/ui/plugin';
 import { AnimationControls } from '../../../mol-plugin/ui/state/animation';
 import { CameraSnapshots } from '../../../mol-plugin/ui/camera';
+import { PluginContext } from '../../../mol-plugin/context';
+import { TransformUpdaterControl } from '../../../mol-plugin/ui/state/update-transform';
+import { StateElements } from '../helpers';
 
 export class ControlsWrapper extends PluginUIComponent {
     render() {
@@ -18,4 +22,11 @@ export class ControlsWrapper extends PluginUIComponent {
             <CameraSnapshots />
         </div>;
     }
+}
+
+export function volumeStreamingControls(plugin: PluginContext, parent: Element) {
+    ReactDOM.render(<PluginContextContainer plugin={plugin}>
+            <TransformUpdaterControl nodeRef={StateElements.VolumeStreaming} />
+        </PluginContextContainer>,
+        parent);
 }
\ No newline at end of file
diff --git a/src/mol-app/component/parameter/boolean.tsx b/src/mol-app/component/parameter/boolean.tsx
index d5ec7529ed8a225b07904a37d7cbf54c64856b68..bb93409e4f24b135c5de0efe6db297d6e92bfdd7 100644
--- a/src/mol-app/component/parameter/boolean.tsx
+++ b/src/mol-app/component/parameter/boolean.tsx
@@ -9,7 +9,7 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 
 export interface BooleanParamComponentProps {
     label: string
-    param: PD.Boolean
+    param: PD.BooleanParam
     value: boolean
     onChange(v: boolean): void
 }
diff --git a/src/mol-geo/geometry/overpaint-data.ts b/src/mol-geo/geometry/overpaint-data.ts
index c256cc8dc9feae640093a59f1945af66c79634aa..b97e19a4abfcc6f8f09cb8c9df01cf58b8082781 100644
--- a/src/mol-geo/geometry/overpaint-data.ts
+++ b/src/mol-geo/geometry/overpaint-data.ts
@@ -24,7 +24,7 @@ export function applyOverpaintColor(array: Uint8Array, start: number, end: numbe
 }
 
 export function clearOverpaint(array: Uint8Array, start: number, end: number) {
-    array.fill(0, start, end)
+    array.fill(0, start * 4, end * 4)
 }
 
 export function createOverpaint(count: number, overpaintData?: OverpaintData): OverpaintData {
diff --git a/src/mol-io/reader/cif/schema/mmcif-extras.ts b/src/mol-io/reader/cif/schema/mmcif-extras.ts
index 10b5e32f74df25b17eaf76414e89f7cd9d98b74b..cb35f4879008bbffd1cc4fbd6c4f577aae29a53a 100644
--- a/src/mol-io/reader/cif/schema/mmcif-extras.ts
+++ b/src/mol-io/reader/cif/schema/mmcif-extras.ts
@@ -1,10 +1,12 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
- import { mmCIF_Schema } from './mmcif';
+import { mmCIF_Schema } from './mmcif';
+import { Column } from '../../../../mol-data/db';
 
 export const mmCIF_residueId_schema = {
     label_comp_id: mmCIF_Schema.atom_site.label_comp_id,
@@ -15,4 +17,10 @@ export const mmCIF_residueId_schema = {
     auth_comp_id: mmCIF_Schema.atom_site.auth_atom_id,
     auth_seq_id: mmCIF_Schema.atom_site.auth_seq_id,
     auth_asym_id: mmCIF_Schema.atom_site.auth_asym_id
+}
+
+export const mmCIF_chemCompBond_schema = {
+    ...mmCIF_Schema.chem_comp_bond,
+    /** Indicates if the bond entry was taken from the protonation variant dictionary */
+    molstar_protonation_variant: Column.Schema.Str()
 }
\ No newline at end of file
diff --git a/src/mol-io/reader/mol2/parser.ts b/src/mol-io/reader/mol2/parser.ts
index 1f727b61fc3982636874e200ac44297234ebf8b0..4e9429f31c8937bb4b4ffa0cafa5d87027aca480 100644
--- a/src/mol-io/reader/mol2/parser.ts
+++ b/src/mol-io/reader/mol2/parser.ts
@@ -84,7 +84,7 @@ function handleMolecule(state: State) {
     molecule.mol_comment = getTokenString(tokenizer)
 }
 
-function isStatus_bit(aString: String): Boolean {
+function isStatus_bit(aString: string): boolean {
     if (aString.includes('DSPMOD') || aString.includes('TYPECOL') || aString.includes('CAP')
         || aString.includes('BACKBONE') || aString.includes('DICT') || aString.includes('ESSENTIAL')
         || aString.includes('WATER') || aString.includes('DIRECT')) {
diff --git a/src/mol-model-props/rcsb/assembly-symmetry.ts b/src/mol-model-props/rcsb/assembly-symmetry.ts
index 150a93ef4766560f6e76e3e3a2c875766e57c7a5..bdf36072cb34acacc21b0eb9a5da5ff30c894363 100644
--- a/src/mol-model-props/rcsb/assembly-symmetry.ts
+++ b/src/mol-model-props/rcsb/assembly-symmetry.ts
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -18,7 +18,6 @@ import { CifCategory } from '../../mol-io/reader/cif';
 import { PropertyWrapper } from '../../mol-model-props/common/wrapper';
 import { Task, RuntimeContext } from '../../mol-task';
 import { GraphQLClient } from '../../mol-util/graphql-client';
-import { ajaxGet } from '../../mol-util/data-source';
 
 const { str, int, float, Aliased, Vector, List } = Column.Schema;
 
@@ -183,13 +182,12 @@ export function AssemblySymmetry(db: AssemblySymmetry.Database): AssemblySymmetr
 type SymmetryKind = 'GLOBAL' | 'LOCAL' | 'PSEUDO'
 type SymmetryType = 'ASYMMETRIC' | 'CYCLIC' | 'DIHEDRAL' | 'HELICAL' | 'ICOSAHEDRAL' | 'OCTAHEDRAL' | 'TETRAHEDRAL'
 
-const Client = new GraphQLClient(AssemblySymmetry.GraphQLEndpointURL, ajaxGet)
-
 export namespace AssemblySymmetry {
     export function is(x: any): x is AssemblySymmetry {
         return x['@type'] === 'rcsb_assembly_symmetry'
     }
-    export const GraphQLEndpointURL = '//rest-dev.rcsb.org/graphql'
+    export const GraphQLEndpointURL = '//rest-staging.rcsb.org/graphql'
+
     export const Schema = {
         rcsb_assembly_symmetry_info: {
             updated_datetime_utc: Column.Schema.str
@@ -257,7 +255,7 @@ export namespace AssemblySymmetry {
 
     export const Descriptor = _Descriptor;
 
-    export async function attachFromCifOrAPI(model: Model, client: GraphQLClient = Client, ctx?: RuntimeContext) {
+    export async function attachFromCifOrAPI(model: Model, client: GraphQLClient, ctx?: RuntimeContext) {
         if (model.customProperties.has(Descriptor)) return true;
 
         let db: Database
@@ -266,8 +264,10 @@ export namespace AssemblySymmetry {
             db = createDatabaseFromCif(model)
         } else {
             let result: AssemblySymmetryGraphQL.Query
+            console.log('model.label.toLowerCase()', model.label.toLowerCase())
             const variables: AssemblySymmetryGraphQL.Variables = { pdbId: model.label.toLowerCase() };
             try {
+                console.log('foo', client)
                 result = await client.request<AssemblySymmetryGraphQL.Query>(ctx || RuntimeContext.Synchronous, query, variables);
             } catch (e) {
                 console.error(e)
diff --git a/src/mol-model-props/wwpdb/chem-comp-bond.ts b/src/mol-model-props/wwpdb/chem-comp-bond.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2395d4832d341d32eb8aa32d44dfbbaab79aafc5
--- /dev/null
+++ b/src/mol-model-props/wwpdb/chem-comp-bond.ts
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Column, Table } from '../../mol-data/db';
+import { toTable } from '../../mol-io/reader/cif/schema';
+import { Model, CustomPropertyDescriptor } from '../../mol-model/structure';
+import { mmCIF_chemCompBond_schema } from '../../mol-io/reader/cif/schema/mmcif-extras';
+import { CifWriter } from '../../mol-io/writer/cif';
+
+export namespace ChemCompBond {
+    export type Property = Table<Schema['chem_comp_bond']>
+
+    export function getFromModel(model: Model): Property {
+        if (model.sourceData.kind !== 'mmCIF') return Table.ofUndefinedColumns(Schema.chem_comp_bond, 0);
+        const { chem_comp_bond } = model.sourceData.data
+        return Table.ofColumns(Schema.chem_comp_bond, {
+            ...chem_comp_bond,
+            molstar_protonation_variant: Column.Undefined(chem_comp_bond._rowCount, Column.Schema.Str())
+        });
+    }
+
+    export function get(model: Model): Property {
+        return model._staticPropertyData.__ChemCompBond__ || getFromModel(model);
+    }
+    function set(model: Model, prop: Property) {
+        (model._staticPropertyData.__ChemCompBond__ as Property) = prop;
+    }
+
+    export const Schema = { chem_comp_bond: mmCIF_chemCompBond_schema };
+    export type Schema = typeof Schema
+
+    export const Descriptor = CustomPropertyDescriptor({
+        isStatic: true,
+        name: 'chem_comp_bond',
+        cifExport: {
+            prefix: '',
+            context(ctx): Property { return get(ctx.firstModel); },
+            categories: [{
+                name: 'chem_comp_bond',
+                instance(ctx: Property) {
+                    return CifWriter.Category.ofTable(ctx);
+                }
+            }]
+        }
+    });
+
+    function fromCifData(model: Model): Table<Schema['chem_comp_bond']> | undefined {
+        if (model.sourceData.kind !== 'mmCIF') return void 0;
+        const cat = model.sourceData.frame.categories.chem_comp_bond;
+        if (!cat) return void 0;
+        return toTable(Schema.chem_comp_bond, cat);
+    }
+
+    export async function attachFromCifOrTable(model: Model, params: {
+        // optional Table source
+        wwPDB_apiSourceTable?: (model: Model) => Promise<Table<Schema['chem_comp_bond']>>
+    }) {
+        if (model.customProperties.has(Descriptor)) return true;
+
+        let chemCompBond: Table<Schema['chem_comp_bond']> | undefined = fromCifData(model);
+        if (chemCompBond === void 0 && params.wwPDB_apiSourceTable) {
+            const data = await params.wwPDB_apiSourceTable(model);
+            if (!data) return false;
+            chemCompBond = chemCompBondFromTable(model, data);
+        } else {
+            return false;
+        }
+
+        if (!chemCompBond) return false;
+
+        model.customProperties.add(Descriptor);
+        set(model, chemCompBond);
+        return true;
+    }
+}
+
+function chemCompBondFromTable(model: Model, table: Table<ChemCompBond.Schema['chem_comp_bond']>): Table<ChemCompBond.Schema['chem_comp_bond']> {
+    return Table.pick(table, ChemCompBond.Schema.chem_comp_bond, (i: number) => {
+        return model.properties.chemicalComponentMap.has(table.comp_id.value(i))
+    })
+}
\ No newline at end of file
diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts
index 50a524740a6dbcf4b59baac01b946519fedada9c..5388a1c473912fb8a279a8a5f125e9a35545087e 100644
--- a/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts
+++ b/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts
@@ -43,13 +43,13 @@ export namespace VolumeStreaming {
         valuesInfo: [{ mean: 0, min: -1, max: 1, sigma: 0.1 }, { mean: 0, min: -1, max: 1, sigma: 0.1 }]
     };
 
-    export function createParams(data?: VolumeServerInfo.Data) {
+    export function createParams(data?: VolumeServerInfo.Data, defaultView?: ViewTypes) {
         // fake the info
         const info = data || { kind: 'em', header: { sampling: [fakeSampling], availablePrecisions: [{ precision: 0, maxVoxels: 0 }] }, emDefaultContourLevel: VolumeIsoValue.relative(0) };
         const box = (data && data.structure.boundary.box) || Box3D.empty();
 
         return {
-            view: PD.MappedStatic(info.kind === 'em' ? 'cell' : 'selection-box', {
+            view: PD.MappedStatic(defaultView || (info.kind === 'em' ? 'cell' : 'selection-box'), {
                 'box': PD.Group({
                     bottomLeft: PD.Vec3(box.min),
                     topRight: PD.Vec3(box.max),
@@ -76,6 +76,8 @@ export namespace VolumeStreaming {
         };
     }
 
+    export type ViewTypes = 'box' | 'selection-box' | 'cell'
+
     export type ParamDefinition = typeof createParams extends (...args: any[]) => (infer T) ? T : never
     export type Params = ParamDefinition extends PD.Params ? PD.Values<ParamDefinition> : {}
 
diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts
index 97b25f4ebc995eb8e4bea2c4aa0459019211c272..bc42398ae2ff2cc4e2137ba8442e4576b40102dc 100644
--- a/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts
+++ b/src/mol-plugin/behavior/dynamic/volume-streaming/transformers.ts
@@ -26,10 +26,13 @@ export const InitVolumeStreaming = StateAction.build({
     display: { name: 'Volume Streaming' },
     from: SO.Molecule.Structure,
     params(a) {
+        const method = getStreamingMethod(a && a.data);
         return {
-            method: PD.Select<VolumeServerInfo.Kind>(getStreamingMethod(a && a.data), [['em', 'EM'], ['x-ray', 'X-Ray']]),
+            method: PD.Select<VolumeServerInfo.Kind>(method, [['em', 'EM'], ['x-ray', 'X-Ray']]),
             id: PD.Text((a && a.data.models.length > 0 && a.data.models[0].label) || ''),
-            serverUrl: PD.Text('https://ds.litemol.org')
+            serverUrl: PD.Text('https://ds.litemol.org'),
+            defaultView: PD.Text<VolumeStreaming.ViewTypes>(method === 'em' ? 'cell' : 'selection-box'),
+            behaviorRef: PD.Text('', { isHidden: true })
         };
     },
     isApplicable: (a) => a.data.models.length === 1
@@ -56,7 +59,8 @@ export const InitVolumeStreaming = StateAction.build({
     const infoObj = await state.updateTree(infoTree).runInContext(taskCtx);
 
     const behTree = state.build().to(infoTree.ref).apply(CreateVolumeStreamingBehavior,
-        PD.getDefaultValues(VolumeStreaming.createParams(infoObj.data)));
+        PD.getDefaultValues(VolumeStreaming.createParams(infoObj.data, params.defaultView)),
+        { ref: params.behaviorRef ? params.behaviorRef : void 0 });
 
     if (params.method === 'em') {
         behTree.apply(VolumeStreamingVisual, { channel: 'em' }, { state: { isGhost: true } });
diff --git a/src/mol-plugin/ui/controls/parameters.tsx b/src/mol-plugin/ui/controls/parameters.tsx
index 19323297adbb3af4c45b2fa741d8851a590750fc..84f9b0742dbd920a11b3f12a6115d9548ffe23d5 100644
--- a/src/mol-plugin/ui/controls/parameters.tsx
+++ b/src/mol-plugin/ui/controls/parameters.tsx
@@ -15,7 +15,7 @@ import { camelCaseToWords } from '../../../mol-util/string';
 import * as React from 'react';
 import LineGraphComponent from './line-graph/line-graph-component';
 import { Slider, Slider2 } from './slider';
-import { NumericInput, IconButton } from './common';
+import { NumericInput, IconButton, ControlGroup } from './common';
 import { _Props, _State } from '../base';
 
 export interface ParameterControlsProps<P extends PD.Params = PD.Params> {
@@ -97,7 +97,7 @@ export abstract class SimpleParam<P extends PD.Any> extends React.PureComponent<
     }
 }
 
-export class BoolControl extends SimpleParam<PD.Boolean> {
+export class BoolControl extends SimpleParam<PD.BooleanParam> {
     onClick = (e: React.MouseEvent<HTMLButtonElement>) => { this.update(!this.props.value); e.currentTarget.blur(); }
     renderControl() {
         return <button onClick={this.onClick} disabled={this.props.isDisabled}>
@@ -461,8 +461,7 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>,
             </div>
             {this.state.isExpanded && <div className='msp-control-offset' style={{ display: this.state.isExpanded ? 'block' : 'none' }}>
                 {controls}
-            </div>
-            }
+            </div>}
         </div>
     }
 }
@@ -655,9 +654,12 @@ export class ObjectListControl extends React.PureComponent<ParamProps<PD.ObjectL
                     <button onClick={this.toggleExpanded}>{value}</button>
                 </div>
             </div>
+
             {this.state.isExpanded && <div className='msp-control-offset'>
                 {this.props.value.map((v, i) => <ObjectListItem key={i} param={this.props.param} value={v} index={i} actions={this.actions} />)}
-                <ObjectListEditor params={this.props.param.element} apply={this.add} value={this.props.param.ctor()} isDisabled={this.props.isDisabled} />
+                <ControlGroup header='New Item'>
+                    <ObjectListEditor params={this.props.param.element} apply={this.add} value={this.props.param.ctor()} isDisabled={this.props.isDisabled} />
+                </ControlGroup>
             </div>}
         </>;
     }
diff --git a/src/mol-plugin/ui/plugin.tsx b/src/mol-plugin/ui/plugin.tsx
index dad27abdbb59757cf1e9fc0a89f24da62b426538..d911bd492272b30235fb3328bf2a34e9c2d5811e 100644
--- a/src/mol-plugin/ui/plugin.tsx
+++ b/src/mol-plugin/ui/plugin.tsx
@@ -37,6 +37,16 @@ export class Plugin extends React.Component<{ plugin: PluginContext }, {}> {
     }
 }
 
+export class PluginContextContainer extends React.Component<{ plugin: PluginContext }> {
+    render() {
+        return <PluginReactContext.Provider value={this.props.plugin}>
+            <div className='msp-plugin'>
+                {this.props.children}
+            </div>
+        </PluginReactContext.Provider>;
+    }
+}
+
 class Layout extends PluginUIComponent {
     componentDidMount() {
         this.subscribe(this.plugin.layout.events.updated, () => this.forceUpdate());
diff --git a/src/mol-plugin/ui/state/update-transform.tsx b/src/mol-plugin/ui/state/update-transform.tsx
index 1e6cd3ba90a9514e94cff73e318b036bad9b6ade..7aafc25eaeffdb5b0feef2a746537fbadf263f15 100644
--- a/src/mol-plugin/ui/state/update-transform.tsx
+++ b/src/mol-plugin/ui/state/update-transform.tsx
@@ -101,7 +101,6 @@ class TransformUpdaterControl extends PluginUIComponent<{ nodeRef: string, initi
         if (!cell || (cell.status !== 'ok' && cell.status !== 'error')) return null;
 
         const transform = cell.transform;
-
         return <UpdateTransformContol state={state} transform={transform} initiallyCollapsed={this.props.initiallyCollapsed} customHeader={this.props.header} />;
     }
 }
\ No newline at end of file
diff --git a/src/mol-util/console-logger.ts b/src/mol-util/console-logger.ts
index c09f08b9aaa412e2f8a9e155fdbb3c01cfe7c8bc..8d04c03fb835ef96ee3bd36d85e8ede19ff35b8c 100644
--- a/src/mol-util/console-logger.ts
+++ b/src/mol-util/console-logger.ts
@@ -25,7 +25,7 @@ export namespace ConsoleLogger {
         console.log(`[${tag}] ${msg}`);
     }
 
-    export function logId(guid: string | String, tag: string, msg: string) {
+    export function logId(guid: string, tag: string, msg: string) {
         console.log(`[${guid}][${tag}] ${msg}`);
     }
 
@@ -38,7 +38,7 @@ export namespace ConsoleLogger {
         console.error(`[Warn] (${ctx}) ${e}`);
     }
 
-    export function errorId(guid: string | String, e: any) {
+    export function errorId(guid: string, e: any) {
         console.error(`[${guid}][Error] ${e}`);
         if (e.stack) console.error(e.stack);
     }
diff --git a/src/mol-util/param-definition.ts b/src/mol-util/param-definition.ts
index 92496fc63e303412c2ebbaada2a99df030b2cb8a..d94f32864c051da74ed8261c21b74f34cbe65ceb 100644
--- a/src/mol-util/param-definition.ts
+++ b/src/mol-util/param-definition.ts
@@ -76,11 +76,11 @@ export namespace ParamDefinition {
         return setInfo<MultiSelect<E, T>>({ type: 'multi-select', defaultValue, options }, info)
     }
 
-    export interface Boolean extends Base<boolean> {
+    export interface BooleanParam extends Base<boolean> {
         type: 'boolean'
     }
-    export function Boolean(defaultValue: boolean, info?: Info): Boolean {
-        return setInfo<Boolean>({ type: 'boolean', defaultValue }, info)
+    export function Boolean(defaultValue: boolean, info?: Info): BooleanParam {
+        return setInfo<BooleanParam>({ type: 'boolean', defaultValue }, info)
     }
 
     export interface Text<T extends string = string> extends Base<T> {
@@ -237,7 +237,7 @@ export namespace ParamDefinition {
     }
 
     export type Any =
-        | Value<any> | Select<any> | MultiSelect<any> | Boolean | Text | Color | Vec3 | Numeric | FileParam | Interval | LineGraph
+        | Value<any> | Select<any> | MultiSelect<any> | BooleanParam | Text | Color | Vec3 | Numeric | FileParam | Interval | LineGraph
         | ColorScale<any> | Group<any> | Mapped<any> | Converted<any, any> | Conditioned<any, any, any> | ScriptExpression | ObjectList
 
     export type Params = { [k: string]: Any }
diff --git a/src/servers/common/util.ts b/src/servers/common/util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3086ffb1f4ee0b69ec51a1a86b15e5326388444e
--- /dev/null
+++ b/src/servers/common/util.ts
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ConsoleLogger } from '../../mol-util/console-logger';
+
+export function getParam<T>(params: any, ...path: string[]): T | undefined {
+    try {
+        let current = params;
+        for (const p of path) {
+            if (typeof current === 'undefined') return;
+            current = current[p];
+        }
+        return current;
+    } catch (e) {
+        ConsoleLogger.error('Config', `Unable to retrieve property ${path.join('.')} from ${JSON.stringify(params)}`);
+    }
+}
\ No newline at end of file
diff --git a/src/servers/model/config.ts b/src/servers/model/config.ts
index ac963eadf6789115e96101880ee7e61449d655bd..860f6a0a436547e5923ffdb8d233b76b90d04e42 100644
--- a/src/servers/model/config.ts
+++ b/src/servers/model/config.ts
@@ -54,8 +54,9 @@ const config = {
      */
     customProperties: <ModelPropertyProviderConfig | string>{
         sources: [
-            './properties/pdbe',
-            './properties/rcsb'
+            'pdbe',
+            'rcsb',
+            'wwpdb'
         ],
         params: {
             PDBe: {
@@ -68,6 +69,14 @@ const config = {
                 File: {
                     residuewise_outlier_summary: 'e:/test/mol-star/model/props/'
                 }
+            },
+            RCSB: {
+                API: {
+                    assembly_symmetry: 'https://rest-staging.rcsb.org/graphql'
+                }
+            },
+            wwPDB: {
+                chemCompBondTablePath: ''
             }
         }
     },
diff --git a/src/servers/model/preprocess/master.ts b/src/servers/model/preprocess/master.ts
index 9ff8b8e1fbb321579029f40963ae563aba7f502a..4c7068208838bd82a9e09ea9c00f9ad23255ad57 100644
--- a/src/servers/model/preprocess/master.ts
+++ b/src/servers/model/preprocess/master.ts
@@ -10,9 +10,27 @@ import * as argparse from 'argparse'
 import { runMaster, PreprocessEntry } from './parallel';
 import { ModelPropertyProviderConfig } from '../property-provider';
 
+function description() {
+    const exampleCfg = {
+        numProcesses: 1,
+        customProperties: {
+            sources: [
+                'wwpdb'
+            ],
+            params: {
+                wwPDB: {
+                    chemCompBondTablePath: './build/data/ccb.bcif'
+                }
+            }
+        }
+    }
+
+    return `Preprocess CIF files to include custom properties and convert them to BinaryCIF format.\n\nExample cfg.json: ${JSON.stringify(exampleCfg, null, 2)}`
+}
+
 const cmdParser = new argparse.ArgumentParser({
     addHelp: true,
-    description: 'Preprocess CIF files to include custom properties and convert them to BinaryCIF format.'
+    description: description()
 });
 cmdParser.addArgument(['--input', '-i'], { help: 'Input filename', required: false });
 cmdParser.addArgument(['--outCIF', '-oc'], { help: 'Output CIF filename', required: false });
diff --git a/src/servers/model/preprocess/preprocess.ts b/src/servers/model/preprocess/preprocess.ts
index 727da1d02398ccfab086b9db97bca3d221fdeeb8..3976748265ff4517f9fbc52df0a4b5e9faf0be56 100644
--- a/src/servers/model/preprocess/preprocess.ts
+++ b/src/servers/model/preprocess/preprocess.ts
@@ -21,7 +21,6 @@ export function preprocessFile(filename: string, propertyProvider?: ModelPropert
         : convert(filename, outputCif, outputBcif);
 }
 
-
 async function preprocess(filename: string, propertyProvider?: ModelPropertiesProvider, outputCif?: string, outputBcif?: string) {
     const input = await readStructureWrapper('entry', '_local_', filename, propertyProvider);
     const categories = await classifyCif(input.cifFrame);
diff --git a/src/servers/model/properties/providers/pdbe.ts b/src/servers/model/properties/providers/pdbe.ts
index f0b254dd0aa98000cb0d7aeb84865f0359fe9664..7935d4a49bd1f971f84240e778d32459e61b7fd4 100644
--- a/src/servers/model/properties/providers/pdbe.ts
+++ b/src/servers/model/properties/providers/pdbe.ts
@@ -14,6 +14,7 @@ import { PDBePreferredAssembly } from '../../../../mol-model-props/pdbe/preferre
 import { PDBeStructRefDomain } from '../../../../mol-model-props/pdbe/struct-ref-domain';
 import { AttachModelProperty } from '../../property-provider';
 import { ConsoleLogger } from '../../../../mol-util/console-logger';
+import { getParam } from '../../../common/util';
 
 export const PDBe_structureQualityReport: AttachModelProperty = ({ model, params, cache }) => {
     const PDBe_apiSourceJson = useFileSource(params)
@@ -68,20 +69,6 @@ function useFileSource(params: any) {
     return !!getParam<boolean>(params, 'PDBe', 'UseFileSource')
 }
 
-function getParam<T>(params: any, ...path: string[]): T | undefined {
-    try {
-        let current = params;
-        for (const p of path) {
-            if (typeof current === 'undefined') return;
-            current = current[p];
-        }
-        return current;
-    } catch (e) {
-        ConsoleLogger.error('Config', `Unable to retrieve property ${path.join('.')} from ${JSON.stringify(params)}`);
-    }
-}
-
-
 function apiQueryProvider(urlPrefix: string, cache: any) {
     const cacheKey = UUID.create22();
     return async (model: Model) => {
diff --git a/src/servers/model/properties/providers/rcsb.ts b/src/servers/model/properties/providers/rcsb.ts
index e2ae21e66dd80fe2487b687600a708524b7fcf7d..011ad4b3cb367457b2c749e723628be487509c76 100644
--- a/src/servers/model/properties/providers/rcsb.ts
+++ b/src/servers/model/properties/providers/rcsb.ts
@@ -1,12 +1,23 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { AssemblySymmetry } from '../../../../mol-model-props/rcsb/assembly-symmetry';
 import { AttachModelProperty } from '../../property-provider';
+import { ajaxGet } from '../../../../mol-util/data-source';
+import { GraphQLClient } from '../../../../mol-util/graphql-client';
+import { getParam } from '../../../common/util';
 
-export const RCSB_assemblySymmetry: AttachModelProperty = ({ model }) => {
-    return AssemblySymmetry.attachFromCifOrAPI(model)
+export const RCSB_assemblySymmetry: AttachModelProperty = ({ model, params }) => {
+    const url = getApiUrl(params, 'assembly_symmetry', `https:${AssemblySymmetry.GraphQLEndpointURL}`)
+    const client = new GraphQLClient(url, ajaxGet)
+    return AssemblySymmetry.attachFromCifOrAPI(model, client)
+}
+
+function getApiUrl(params: any, name: string, fallback: string) {
+    const path = getParam<string>(params, 'RCSB', 'API', name);
+    if (!path) return fallback;
+    return path;
 }
\ No newline at end of file
diff --git a/src/servers/model/properties/providers/wwpdb.ts b/src/servers/model/properties/providers/wwpdb.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d5ea7136e5a082ff8b7140f74fd9e7a2d21e6b62
--- /dev/null
+++ b/src/servers/model/properties/providers/wwpdb.ts
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import * as fs from 'fs'
+import * as util from 'util'
+import { AttachModelProperty } from '../../property-provider';
+import { ChemCompBond } from '../../../../mol-model-props/wwpdb/chem-comp-bond';
+import { Table } from '../../../../mol-data/db';
+import { CIF } from '../../../../mol-io/reader/cif';
+import { getParam } from '../../../common/util';
+
+require('util.promisify').shim()
+const readFile = util.promisify(fs.readFile)
+
+export const wwPDB_chemCompBond: AttachModelProperty = ({ model, params }) => {
+    const wwPDB_apiSourceTable = getChemCompBondTableProvider(getTablePath(params))
+    return ChemCompBond.attachFromCifOrTable(model, { wwPDB_apiSourceTable });
+}
+
+async function read(path: string) {
+    return path.endsWith('.bcif') ? new Uint8Array(await readFile(path)) : readFile(path, 'utf8');
+}
+
+function getChemCompBondTableProvider(path: string): () => Promise<Table<ChemCompBond.Schema['chem_comp_bond']>> {
+    let chemCompBondTable: Table<ChemCompBond.Schema['chem_comp_bond']>
+    return async function() {
+        if (chemCompBondTable === undefined) {
+            const parsed = await CIF.parse(await read(path)).run()
+            if (parsed.isError) throw new Error(parsed.toString())
+            const table = CIF.toDatabase(ChemCompBond.Schema, parsed.result.blocks[0])
+            chemCompBondTable = table.chem_comp_bond
+        }
+        return chemCompBondTable
+    }
+}
+
+function getTablePath(params: any) {
+    const path = getParam<string>(params, 'wwPDB', 'chemCompBondTablePath');
+    if (!path) throw new Error(`wwPDB 'chemCompBondTablePath' not set!`);
+    return path;
+}
\ No newline at end of file
diff --git a/src/servers/model/properties/wwpdb.ts b/src/servers/model/properties/wwpdb.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cb4040db1f7ac0ac78899f42d472e24113019bd9
--- /dev/null
+++ b/src/servers/model/properties/wwpdb.ts
@@ -0,0 +1,16 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { AttachModelProperties } from '../property-provider';
+import { wwPDB_chemCompBond } from './providers/wwpdb';
+
+export const attachModelProperties: AttachModelProperties = (args) => {
+    // return a list of promises that start attaching the props in parallel
+    // (if there are downloads etc.)
+    return [
+        wwPDB_chemCompBond(args)
+    ];
+}
\ No newline at end of file
diff --git a/src/servers/model/property-provider.ts b/src/servers/model/property-provider.ts
index d3b4ad00a0d836192f02361b408ee31ad3f189a4..d929c30c7320093b85d5b4677fe889fcb7c3b81f 100644
--- a/src/servers/model/property-provider.ts
+++ b/src/servers/model/property-provider.ts
@@ -9,6 +9,17 @@ import { Model } from '../../mol-model/structure';
 import Config from './config';
 import { ConsoleLogger } from '../../mol-util/console-logger';
 
+// TODO enable dynamic imports again
+import * as pdbeProps from './properties/pdbe'
+import * as rcsbProps from './properties/rcsb'
+import * as wwpdbProps from './properties/wwpdb'
+
+const attachModelProperties: { [k: string]: AttachModelProperties } = {
+    pdbe: pdbeProps.attachModelProperties,
+    rcsb: rcsbProps.attachModelProperties,
+    wwpdb: wwpdbProps.attachModelProperties
+}
+
 export interface ModelPropertyProviderConfig {
     sources: string[],
     params?: { [name: string]: any }
@@ -39,7 +50,11 @@ export function createModelPropertiesProvider(configOrPath: ModelPropertyProvide
 
     const ps: AttachModelProperties[] = [];
     for (const p of config.sources) {
-        ps.push(require(p).attachModelProperties);
+        if (p in attachModelProperties) {
+            ps.push(attachModelProperties[p]);
+        } else {
+            ConsoleLogger.error('Config', `Could not find property provider '${p}', ignoring.`);
+        }
     }
 
     return (model, cache) => {
diff --git a/src/servers/volume/local.ts b/src/servers/volume/local.ts
index 99993cfbcfa10612463f83325ff959db7cd93f43..f2145c2badfc82440a9a4ed0ed1c2cc68bc2aa76 100644
--- a/src/servers/volume/local.ts
+++ b/src/servers/volume/local.ts
@@ -16,7 +16,7 @@ import { LimitsConfig, addLimitsArgs, setLimitsConfig } from './config';
 console.log(`VolumeServer Local ${VERSION}, (c) 2018-2019, Mol* contributors`);
 console.log();
 
-function help() {
+function description() {
     const exampleJobs: LocalApi.JobEntry[] = [{
         source: {
             filename: `g:/test/mdb/xray-1tqn.mdb`,
@@ -55,7 +55,7 @@ function help() {
 
 const parser = new argparse.ArgumentParser({
     addHelp: true,
-    description: help()
+    description: description()
 });
 addLimitsArgs(parser)
 parser.addArgument(['jobs'], {
diff --git a/tslint.json b/tslint.json
index 1c88592ea6631a73315ede0d282296f9626b68c6..ecf0639ee5c4ae7ba5c27ed412efe60d3867995c 100644
--- a/tslint.json
+++ b/tslint.json
@@ -6,6 +6,7 @@
         ],
         "arrow-parens": false,
         "no-var-keyword": true,
+        "no-construct": true,
         "ordered-imports": [false],
         "trailing-comma": [false],
         "class-name": false,
@@ -17,6 +18,12 @@
             true,
             "spaces"
         ],
+        "ban-types": [
+            true,
+            ["String", "Use primitive 'string' instead."],
+            ["Boolean", "Use primitive 'boolean' instead."],
+            ["Number", "Use primitive 'number' instead."]
+        ],
         "no-eval": true,
         "no-internal-module": true,
         "no-trailing-whitespace": true,
diff --git a/webpack.config.js b/webpack.config.js
index 3de9e1565111c706a5f326ff84f7f9f143cc4e28..9c4d98c40544b636d4b16b027b416707bbaff62d 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -8,16 +8,19 @@ const sharedConfig = {
     module: {
         rules: [
             {
-                loader: 'file-loader',
                 test: /\.(woff2?|ttf|otf|eot|svg|html)$/,
-                include: [path.resolve(__dirname, 'lib/')],
-                options: {
-                    name: '[name].[ext]'
-                }
+                include: [path.resolve(__dirname, 'build/src/')],
+                use: [{
+                    loader: 'file-loader',
+                    options: { name: '[name].[ext]' }
+                }]
             },
             {
                 test: /\.(s*)css$/,
-                use: [MiniCssExtractPlugin.loader, 'css-loader', 'resolve-url-loader', 'sass-loader']
+                use: [
+                    MiniCssExtractPlugin.loader,
+                    'css-loader', 'resolve-url-loader', 'sass-loader'
+                ]
             }
         ]
     },
@@ -73,6 +76,12 @@ function createNodeEntryPoint(name, dir, out) {
         target: 'node',
         entry: path.resolve(__dirname, `lib/${dir}/${name}.js`),
         output: { filename: `${name}.js`, path: path.resolve(__dirname, `build/${out}`) },
+        externals: {
+            argparse: 'require("argparse")',
+            'node-fetch': 'require("node-fetch")',
+            'util.promisify': 'require("util.promisify")',
+            xhr2: 'require("xhr2")',
+        },
         ...sharedConfig
     }
 }