diff --git a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts
index 7d65aec3a074092a47986d592b4a2b91bf990556..eb65b9e8ddad298fb886976ea0d811693b3c5009 100644
--- a/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts
+++ b/src/mol-geo/representation/structure/visual/carbohydrate-symbol-mesh.ts
@@ -21,7 +21,7 @@ import { SizeTheme } from '../../../theme';
 import { createMeshValues, updateMeshValues, updateRenderableState, createRenderableState, DefaultMeshProps } from '../../util';
 import { MeshBuilder } from '../../../shape/mesh-builder';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
-import { createUniformColor } from '../../../util/color-data';
+import { createValueColor } from '../../../util/color-data';
 import { getSaccharideShape, SaccharideShapes } from 'mol-model/structure/structure/carbohydrates/constants';
 
 const t = Mat4.identity()
@@ -173,7 +173,7 @@ export function CarbohydrateSymbolVisual(): StructureVisual<CarbohydrateSymbolPr
             // console.log(mesh)
 
             const transforms = createIdentityTransform()
-            const color = createUniformColor({ value: 0x999911 }) // TODO
+            const color = createValueColor(0x999911) // TODO
             const marker = createMarkers(instanceCount * elementCount)
 
             const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
diff --git a/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts b/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts
index 2500032d1f36e12df8e111b1336f786e62c6736c..ea48347d29c81fb83e3d897f7396e9f6e6b120a2 100644
--- a/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/cross-link-restraint-cylinder.ts
@@ -7,7 +7,7 @@
 import { ValueCell } from 'mol-util/value-cell'
 
 import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
-import { Link, Structure } from 'mol-model/structure';
+import { Link, Structure, StructureElement } from 'mol-model/structure';
 import { DefaultStructureProps, StructureVisual } from '..';
 import { RuntimeContext } from 'mol-task'
 import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh } from './util/link';
@@ -16,7 +16,7 @@ import { getMeshData } from '../../../util/mesh-data';
 import { Mesh } from '../../../shape/mesh';
 import { PickingId } from '../../../util/picking';
 import { Vec3 } from 'mol-math/linear-algebra';
-import { createUniformColor } from '../../../util/color-data';
+import { createValueColor } from '../../../util/color-data';
 import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
 import { MarkerAction, applyMarkerAction, createMarkers, MarkerData } from '../../../util/marker-data';
 import { SizeTheme } from '../../../theme';
@@ -74,7 +74,7 @@ export function CrossLinkRestraintVisual(): StructureVisual<CrossLinkRestraintPr
             mesh = await createCrossLinkRestraintCylinderMesh(ctx, structure, currentProps)
 
             const transforms = createIdentityTransform()
-            const color = createUniformColor({ value: 0x119911 }) // TODO
+            const color = createValueColor(0x119911) // TODO
             const marker = createMarkers(instanceCount * elementCount)
 
             const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
@@ -121,12 +121,12 @@ function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
     if (id === objectId) {
         const pair = structure.crossLinkRestraints.pairs[elementId]
         if (pair) {
-            return Link.Loci([{
-                aUnit: pair.unitA,
-                aIndex: pair.indexA,
-                bUnit: pair.unitB,
-                bIndex: pair.indexB
-            }])
+            return Link.Loci([
+                Link.Location(
+                    pair.unitA, pair.indexA as StructureElement.UnitIndex,
+                    pair.unitB, pair.indexB as StructureElement.UnitIndex
+                )
+            ])
         }
     }
     return EmptyLoci
diff --git a/src/mol-geo/representation/structure/visual/element-point.ts b/src/mol-geo/representation/structure/visual/element-point.ts
index 22218af48ee3729a0becd19eedfa40463d3ada5c..55a4b30ebb6289a6eb1e0e53bb486dc249ce0c82 100644
--- a/src/mol-geo/representation/structure/visual/element-point.ts
+++ b/src/mol-geo/representation/structure/visual/element-point.ts
@@ -23,6 +23,7 @@ import { Loci } from 'mol-model/loci';
 import { MarkerAction, createMarkers } from '../../../util/marker-data';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { fillSerial } from 'mol-util/array';
+import { StructureElementIterator } from './util/location-iterator';
 
 export const DefaultPointProps = {
     ...DefaultStructureProps,
@@ -78,7 +79,7 @@ export default function PointVisual(): UnitsVisual<PointProps> {
 
             const vertices = createPointVertices(_units[0])
             const transforms = createTransforms(group)
-            const color = createColors(group, elementCount, colorTheme)
+            const color = createColors(StructureElementIterator.fromGroup(group), colorTheme)
             const size = createSizes(group, vertexMap, sizeTheme)
             const marker = createMarkers(instanceCount * elementCount)
 
diff --git a/src/mol-geo/representation/structure/visual/element-sphere.ts b/src/mol-geo/representation/structure/visual/element-sphere.ts
index 53a2a3c0ed76e6334a14d9167c54a92e13881a1a..f1680f02cdbbb12c9a90c768ae80fe2c3569a4f5 100644
--- a/src/mol-geo/representation/structure/visual/element-sphere.ts
+++ b/src/mol-geo/representation/structure/visual/element-sphere.ts
@@ -22,6 +22,7 @@ import { createMarkers, MarkerAction } from '../../../util/marker-data';
 import { Loci } from 'mol-model/loci';
 import { SizeTheme } from '../../../theme';
 import { createMeshValues, updateMeshValues, updateRenderableState, createRenderableState, DefaultMeshProps } from '../../util';
+import { StructureElementIterator } from './util/location-iterator';
 
 export const DefaultElementSphereProps = {
     ...DefaultMeshProps,
@@ -55,7 +56,7 @@ export function ElementSphereVisual(): UnitsVisual<ElementSphereProps> {
                 : Mesh.createEmpty(mesh)
 
             const transforms = createTransforms(group)
-            const color = createColors(group, elementCount, colorTheme)
+            const color = createColors(StructureElementIterator.fromGroup(group), colorTheme)
             const marker = createMarkers(instanceCount * elementCount)
 
             const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
@@ -92,9 +93,7 @@ export function ElementSphereVisual(): UnitsVisual<ElementSphereProps> {
             }
 
             if (updateColor) {
-                const elementCount = currentGroup.elements.length
-                if (ctx.shouldUpdate) await ctx.update('Computing sphere colors');
-                createColors(currentGroup, elementCount, newProps.colorTheme, renderObject.values)
+                createColors(StructureElementIterator.fromGroup(currentGroup), newProps.colorTheme, renderObject.values)
             }
 
             updateMeshValues(renderObject.values, newProps)
diff --git a/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts b/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts
index 69c898238054ddfb9e1b7a77994277f1b0a8820c..585fd7c9979348967fd913ebb9b0dc76f92f07a6 100644
--- a/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/inter-unit-link-cylinder.ts
@@ -7,7 +7,7 @@
 import { ValueCell } from 'mol-util/value-cell'
 
 import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
-import { Link, Structure } from 'mol-model/structure';
+import { Link, Structure, StructureElement } from 'mol-model/structure';
 import { DefaultStructureProps, StructureVisual } from '..';
 import { RuntimeContext } from 'mol-task'
 import { LinkCylinderProps, DefaultLinkCylinderProps, createLinkCylinderMesh } from './util/link';
@@ -16,13 +16,13 @@ import { getMeshData } from '../../../util/mesh-data';
 import { Mesh } from '../../../shape/mesh';
 import { PickingId } from '../../../util/picking';
 import { Vec3 } from 'mol-math/linear-algebra';
-import { createUniformColor } from '../../../util/color-data';
 import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
 import { MarkerAction, applyMarkerAction, createMarkers, MarkerData } from '../../../util/marker-data';
 import { SizeTheme } from '../../../theme';
-import { createIdentityTransform } from './util/common';
+import { createIdentityTransform, createColors } from './util/common';
 import { updateMeshValues, updateRenderableState, createMeshValues, createRenderableState } from '../../util';
-// import { chainIdLinkColorData } from '../../../theme/structure/color/chain-id';
+import { LinkIterator } from './util/location-iterator';
+import { deepEqual } from 'mol-util';
 
 async function createInterUnitLinkCylinderMesh(ctx: RuntimeContext, structure: Structure, props: LinkCylinderProps, mesh?: Mesh) {
     const links = structure.links
@@ -65,13 +65,14 @@ export function InterUnitLinkVisual(): StructureVisual<InterUnitLinkProps> {
             currentProps = Object.assign({}, DefaultInterUnitLinkProps, props)
             currentStructure = structure
 
+            const { colorTheme } = { ...DefaultInterUnitLinkProps, ...props }
             const elementCount = structure.links.bondCount
             const instanceCount = 1
 
             mesh = await createInterUnitLinkCylinderMesh(ctx, structure, currentProps)
 
             const transforms = createIdentityTransform()
-            const color = createUniformColor({ value: 0x999911 }) // TODO
+            const color = createColors(LinkIterator.fromStructure(structure), colorTheme) // TODO
             const marker = createMarkers(instanceCount * elementCount)
 
             const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
@@ -93,12 +94,23 @@ export function InterUnitLinkVisual(): StructureVisual<InterUnitLinkProps> {
 
             if (!renderObject) return false
 
+            let updateColor = false
+
             // TODO create in-place
             if (currentProps.radialSegments !== newProps.radialSegments) return false
 
+            if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) {
+                updateColor = true
+            }
+
+            if (updateColor) {
+                createColors(LinkIterator.fromStructure(currentStructure), newProps.colorTheme, renderObject.values)
+            }
+
             updateMeshValues(renderObject.values, newProps)
             updateRenderableState(renderObject.state, newProps)
 
+            currentProps = newProps
             return false
         },
         getLoci(pickingId: PickingId) {
@@ -117,12 +129,12 @@ function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
     const { objectId, elementId } = pickingId
     if (id === objectId) {
         const bond = structure.links.bonds[elementId]
-        return Link.Loci([{
-            aUnit: bond.unitA,
-            aIndex: bond.indexA,
-            bUnit: bond.unitB,
-            bIndex: bond.indexB
-        }])
+        return Link.Loci([
+            Link.Location(
+                bond.unitA, bond.indexA as StructureElement.UnitIndex,
+                bond.unitB, bond.indexB as StructureElement.UnitIndex
+            )
+        ])
     }
     return EmptyLoci
 }
diff --git a/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts b/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts
index 2a519bbf3bc1af6c0c5abcc78aa1b6214776dac7..fc4dcaf3eec07b10bbe092245590bbcca8c047c5 100644
--- a/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/intra-unit-link-cylinder.ts
@@ -8,7 +8,7 @@
 import { ValueCell } from 'mol-util/value-cell'
 
 import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object'
-import { Unit, Link } from 'mol-model/structure';
+import { Unit, Link, StructureElement } from 'mol-model/structure';
 import { UnitsVisual, DefaultStructureProps } from '..';
 import { RuntimeContext } from 'mol-task'
 import { DefaultLinkCylinderProps, LinkCylinderProps, createLinkCylinderMesh } from './util/link';
@@ -21,9 +21,10 @@ import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
 import { MarkerAction, applyMarkerAction, createMarkers, MarkerData } from '../../../util/marker-data';
 import { SizeTheme } from '../../../theme';
-import { chainIdLinkColorData } from '../../../theme/structure/color/chain-id';
-import { createTransforms } from './util/common';
-import { createMeshValues, createRenderableState, updateMeshValues, updateRenderableState } from '../../util';
+import { createTransforms, createColors } from './util/common';
+import { createMeshValues, createRenderableState, updateMeshValues, updateRenderableState, DefaultMeshProps } from '../../util';
+import { LinkIterator } from './util/location-iterator';
+import { deepEqual } from 'mol-util';
 
 async function createIntraUnitLinkCylinderMesh(ctx: RuntimeContext, unit: Unit, props: LinkCylinderProps, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
@@ -63,6 +64,7 @@ async function createIntraUnitLinkCylinderMesh(ctx: RuntimeContext, unit: Unit,
 }
 
 export const DefaultIntraUnitLinkProps = {
+    ...DefaultMeshProps,
     ...DefaultStructureProps,
     ...DefaultLinkCylinderProps,
     sizeTheme: { name: 'physical', factor: 0.3 } as SizeTheme,
@@ -81,6 +83,7 @@ export function IntraUnitLinkVisual(): UnitsVisual<IntraUnitLinkProps> {
             currentProps = Object.assign({}, DefaultIntraUnitLinkProps, props)
             currentGroup = group
 
+            const { colorTheme } = { ...DefaultIntraUnitLinkProps, ...props }
             const unit = group.units[0]
             const elementCount = Unit.isAtomic(unit) ? unit.links.edgeCount * 2 : 0
             const instanceCount = group.units.length
@@ -88,7 +91,7 @@ export function IntraUnitLinkVisual(): UnitsVisual<IntraUnitLinkProps> {
             mesh = await createIntraUnitLinkCylinderMesh(ctx, unit, currentProps)
 
             const transforms = createTransforms(group)
-            const color = chainIdLinkColorData({ group, elementCount }) // TODO
+            const color = createColors(LinkIterator.fromGroup(currentGroup), colorTheme)
             const marker = createMarkers(instanceCount * elementCount)
 
             const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
@@ -110,12 +113,23 @@ export function IntraUnitLinkVisual(): UnitsVisual<IntraUnitLinkProps> {
 
             if (!renderObject) return false
 
+            let updateColor = false
+
             // TODO create in-place
             if (currentProps.radialSegments !== newProps.radialSegments) return false
 
+            if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) {
+                updateColor = true
+            }
+
+            if (updateColor) {
+                createColors(LinkIterator.fromGroup(currentGroup), newProps.colorTheme, renderObject.values)
+            }
+
             updateMeshValues(renderObject.values, newProps)
             updateRenderableState(renderObject.state, newProps)
 
+            currentProps = newProps
             return true
         },
         getLoci(pickingId: PickingId) {
@@ -134,12 +148,12 @@ function getLinkLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number
     const { objectId, instanceId, elementId } = pickingId
     const unit = group.units[instanceId]
     if (id === objectId && Unit.isAtomic(unit)) {
-        return Link.Loci([{
-            aUnit: unit,
-            aIndex: unit.links.a[elementId],
-            bUnit: unit,
-            bIndex: unit.links.b[elementId]
-        }])
+        return Link.Loci([
+            Link.Location(
+                unit, unit.links.a[elementId] as StructureElement.UnitIndex,
+                unit, unit.links.b[elementId] as StructureElement.UnitIndex
+            )
+        ])
     }
     return EmptyLoci
 }
diff --git a/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts b/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts
index dc081e27d32eafcc7cd82e70bcddc3fc39923e91..bf55d079d25cd346fc2436638986917589fb5e01 100644
--- a/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts
+++ b/src/mol-geo/representation/structure/visual/nucleotide-block-mesh.ts
@@ -26,6 +26,7 @@ import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { Segmentation, SortedArray } from 'mol-data/int';
 import { MoleculeType, isNucleic, isPurinBase, isPyrimidineBase } from 'mol-model/structure/model/types';
 import { getElementIndexForAtomId, getElementIndexForAtomRole } from 'mol-model/structure/util';
+import { StructureElementIterator } from './util/location-iterator';
 
 const p1 = Vec3.zero()
 const p2 = Vec3.zero()
@@ -145,7 +146,7 @@ export function NucleotideBlockVisual(): UnitsVisual<NucleotideBlockProps> {
             // console.log(mesh)
 
             const transforms = createTransforms(group)
-            const color = createColors(group, elementCount, colorTheme)
+            const color = createColors(StructureElementIterator.fromGroup(group), colorTheme)
             const marker = createMarkers(instanceCount * elementCount)
 
             const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
@@ -182,9 +183,8 @@ export function NucleotideBlockVisual(): UnitsVisual<NucleotideBlockProps> {
             }
 
             if (updateColor) {
-                const elementCount = currentGroup.elements.length
                 if (ctx.shouldUpdate) await ctx.update('Computing nucleotide block colors');
-                createColors(currentGroup, elementCount, newProps.colorTheme, renderObject.values)
+                createColors(StructureElementIterator.fromGroup(currentGroup), newProps.colorTheme, renderObject.values)
             }
 
             updateMeshValues(renderObject.values, newProps)
diff --git a/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts b/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts
index 956fe0fdb0f5a210238eab2895f8de150cbe041c..518e0ce126dbf1cc9803773baae3ebac0577d683 100644
--- a/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/polymer-backbone-cylinder.ts
@@ -24,6 +24,7 @@ import { MeshBuilder } from '../../../shape/mesh-builder';
 import { getPolymerElementCount, PolymerBackboneIterator } from './util/polymer';
 import { getElementLoci, markElement } from './util/element';
 import { Vec3 } from 'mol-math/linear-algebra';
+import { StructureElementIterator } from './util/location-iterator';
 
 async function createPolymerBackboneCylinderMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) {
     const polymerElementCount = getPolymerElementCount(unit)
@@ -91,7 +92,7 @@ export function PolymerBackboneVisual(): UnitsVisual<PolymerBackboneProps> {
             // console.log(mesh)
 
             const transforms = createTransforms(group)
-            const color = createColors(group, elementCount, colorTheme)
+            const color = createColors(StructureElementIterator.fromGroup(group), colorTheme)
             const marker = createMarkers(instanceCount * elementCount)
 
             const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
@@ -128,9 +129,8 @@ export function PolymerBackboneVisual(): UnitsVisual<PolymerBackboneProps> {
             }
 
             if (updateColor) {
-                const elementCount = currentGroup.elements.length
                 if (ctx.shouldUpdate) await ctx.update('Computing trace colors');
-                createColors(currentGroup, elementCount, newProps.colorTheme, renderObject.values)
+                createColors(StructureElementIterator.fromGroup(currentGroup), newProps.colorTheme, renderObject.values)
             }
 
             updateMeshValues(renderObject.values, newProps)
diff --git a/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts b/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts
index 76390e3efe676e43233bac93c2324863712ea81f..fcd5833158860fede9987d878ee55fd517574e30 100644
--- a/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts
+++ b/src/mol-geo/representation/structure/visual/polymer-direction-wedge.ts
@@ -26,6 +26,7 @@ import { MeshBuilder } from '../../../shape/mesh-builder';
 import { getPolymerElementCount, PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment } from './util/polymer';
 import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { SecondaryStructureType, MoleculeType } from 'mol-model/structure/model/types';
+import { StructureElementIterator } from './util/location-iterator';
 
 const t = Mat4.identity()
 const sVec = Vec3.zero()
@@ -118,7 +119,7 @@ export function PolymerDirectionVisual(): UnitsVisual<PolymerDirectionProps> {
                 : Mesh.createEmpty(mesh)
 
             const transforms = createTransforms(group)
-            const color = createColors(group, elementCount, colorTheme)
+            const color = createColors(StructureElementIterator.fromGroup(group), colorTheme)
             const marker = createMarkers(instanceCount * elementCount)
 
             const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
@@ -154,9 +155,8 @@ export function PolymerDirectionVisual(): UnitsVisual<PolymerDirectionProps> {
             }
 
             if (updateColor) {
-                const elementCount = currentGroup.elements.length
                 if (ctx.shouldUpdate) await ctx.update('Computing direction colors');
-                createColors(currentGroup, elementCount, newProps.colorTheme, renderObject.values)
+                createColors(StructureElementIterator.fromGroup(currentGroup), newProps.colorTheme, renderObject.values)
             }
 
             updateMeshValues(renderObject.values, newProps)
diff --git a/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts b/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts
index d1f289ec99b41a30b4356e43b2dfbf80da4f2a22..4940c5f5318b55a8b8c93cedb6f4e1730208736e 100644
--- a/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts
+++ b/src/mol-geo/representation/structure/visual/polymer-gap-cylinder.ts
@@ -24,6 +24,7 @@ import { MeshBuilder } from '../../../shape/mesh-builder';
 import { getPolymerGapCount, PolymerGapIterator } from './util/polymer';
 import { getElementLoci, markElement } from './util/element';
 import { Vec3 } from 'mol-math/linear-algebra';
+import { StructureElementIterator } from './util/location-iterator';
 
 async function createPolymerGapCylinderMesh(ctx: RuntimeContext, unit: Unit, mesh?: Mesh) {
     const polymerGapCount = getPolymerGapCount(unit)
@@ -97,7 +98,7 @@ export function PolymerGapVisual(): UnitsVisual<PolymerGapProps> {
             // console.log(mesh)
 
             const transforms = createTransforms(group)
-            const color = createColors(group, elementCount, colorTheme)
+            const color = createColors(StructureElementIterator.fromGroup(group), colorTheme)
             const marker = createMarkers(instanceCount * elementCount)
 
             const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
@@ -134,9 +135,8 @@ export function PolymerGapVisual(): UnitsVisual<PolymerGapProps> {
             }
 
             if (updateColor) {
-                const elementCount = currentGroup.elements.length
                 if (ctx.shouldUpdate) await ctx.update('Computing trace colors');
-                createColors(currentGroup, elementCount, newProps.colorTheme, renderObject.values)
+                createColors(StructureElementIterator.fromGroup(currentGroup), newProps.colorTheme, renderObject.values)
             }
 
             updateMeshValues(renderObject.values, newProps)
diff --git a/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts b/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts
index 8d97c06f0de86a18d7f00b87c31afb35a78768ae..3b37589e8d3e3a580c196f553b1c64c0dcce3d46 100644
--- a/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts
+++ b/src/mol-geo/representation/structure/visual/polymer-trace-mesh.ts
@@ -25,6 +25,7 @@ import { createMeshValues, updateMeshValues, updateRenderableState, createRender
 import { MeshBuilder } from '../../../shape/mesh-builder';
 import { getPolymerElementCount, PolymerTraceIterator, createCurveSegmentState, interpolateCurveSegment } from './util/polymer';
 import { SecondaryStructureType, MoleculeType } from 'mol-model/structure/model/types';
+import { StructureElementIterator } from './util/location-iterator';
 
 // TODO handle polymer ends properly
 
@@ -111,7 +112,7 @@ export function PolymerTraceVisual(): UnitsVisual<PolymerTraceProps> {
                 : Mesh.createEmpty(mesh)
 
             const transforms = createTransforms(group)
-            const color = createColors(group, elementCount, colorTheme)
+            const color = createColors(StructureElementIterator.fromGroup(group), colorTheme)
             const marker = createMarkers(instanceCount * elementCount)
 
             const counts = { drawCount: mesh.triangleCount * 3, elementCount, instanceCount }
@@ -147,9 +148,8 @@ export function PolymerTraceVisual(): UnitsVisual<PolymerTraceProps> {
             }
 
             if (updateColor) {
-                const elementCount = currentGroup.elements.length
                 if (ctx.shouldUpdate) await ctx.update('Computing trace colors');
-                createColors(currentGroup, elementCount, newProps.colorTheme, renderObject.values)
+                createColors(StructureElementIterator.fromGroup(currentGroup), newProps.colorTheme, renderObject.values)
             }
 
             updateMeshValues(renderObject.values, newProps)
diff --git a/src/mol-geo/representation/structure/visual/util/common.ts b/src/mol-geo/representation/structure/visual/util/common.ts
index 4b8c39719436836067f0a97b2c7d63dd918cc9d1..e2e43030fd29a438e04924175c920d5541c63d9e 100644
--- a/src/mol-geo/representation/structure/visual/util/common.ts
+++ b/src/mol-geo/representation/structure/visual/util/common.ts
@@ -13,8 +13,9 @@ import { createUniformSize, SizeData } from '../../../../util/size-data';
 import { physicalSizeData } from '../../../../theme/structure/size/physical';
 import VertexMap from '../../../../shape/vertex-map';
 import { ColorTheme, SizeTheme } from '../../../../theme';
-import { elementIndexColorData, elementSymbolColorData, instanceIndexColorData, chainIdElementColorData } from '../../../../theme/structure/color';
+import { elementIndexColorData, elementSymbolColorData, instanceIndexColorData, chainIdColorData } from '../../../../theme/structure/color';
 import { ValueCell, defaults } from 'mol-util';
+import { LocationIterator } from './location-iterator';
 
 export function createTransforms({ units }: Unit.SymmetryGroup, transforms?: ValueCell<Float32Array>) {
     const unitCount = units.length
@@ -32,18 +33,18 @@ export function createIdentityTransform(transforms?: ValueCell<Float32Array>) {
     return transforms ? ValueCell.update(transforms, identityTransform) : ValueCell.create(identityTransform)
 }
 
-export function createColors(group: Unit.SymmetryGroup, elementCount: number, props: ColorTheme, colorData?: ColorData) {
+export function createColors(locationIt: LocationIterator, props: ColorTheme, colorData?: ColorData) {
     switch (props.name) {
         case 'atom-index':
-            return elementIndexColorData({ group, elementCount }, colorData)
+            return elementIndexColorData(locationIt, colorData)
         case 'chain-id':
-            return chainIdElementColorData({ group, elementCount }, colorData)
+            return chainIdColorData(locationIt, colorData)
         case 'element-symbol':
-            return elementSymbolColorData({ group, elementCount }, colorData)
+            return elementSymbolColorData(locationIt, colorData)
         case 'instance-index':
-            return instanceIndexColorData({ group, elementCount }, colorData)
+            return instanceIndexColorData(locationIt, colorData)
         case 'uniform':
-            return createUniformColor(props, colorData)
+            return createUniformColor(locationIt, () => props.value, colorData)
     }
 }
 
diff --git a/src/mol-geo/representation/structure/visual/util/location-iterator.ts b/src/mol-geo/representation/structure/visual/util/location-iterator.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5dc3d76ce9380de088e6119900ea4ec895edcc6c
--- /dev/null
+++ b/src/mol-geo/representation/structure/visual/util/location-iterator.ts
@@ -0,0 +1,123 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Iterator } from 'mol-data';
+import { Unit, StructureElement, Structure, Link } from 'mol-model/structure';
+import { NullLocation, Location } from 'mol-model/location';
+
+export interface LocationValue {
+    location: Location
+    index: number
+    elementIndex: number
+    instanceIndex: number
+}
+
+export const NullLocationValue = {
+    location: NullLocation,
+    index: 0,
+    elementIndex: 0,
+    instanceIndex: 0
+}
+
+export interface LocationIterator extends Iterator<LocationValue> {
+    readonly hasNext: boolean
+    readonly isNextNewInstance: boolean
+    readonly elementCount: number
+    readonly instanceCount: number
+    move(): LocationValue
+    skipInstance(): void
+}
+
+type LocationGetter = (elementIndex: number, instanceIndex: number) => Location
+
+export function LocationIterator(elementCount: number, instanceCount: number, getLocation: LocationGetter): LocationIterator {
+    const value = {
+        location: NullLocation as Location,
+        index: 0,
+        elementIndex: 0,
+        instanceIndex: 0
+    }
+
+    let hasNext = value.elementIndex < elementCount
+    let isNextNewInstance = false
+    let elementIndex = 0
+    let instanceIndex = 0
+
+    return {
+        get hasNext () { return hasNext },
+        get isNextNewInstance () { return isNextNewInstance },
+        get elementCount () { return elementCount },
+        get instanceCount () { return instanceCount },
+        move() {
+            if (hasNext) {
+                value.elementIndex = elementIndex
+                value.instanceIndex = instanceIndex
+                value.index = instanceIndex * elementCount + elementIndex
+                value.location = getLocation(elementIndex, instanceIndex)
+                ++elementIndex
+                if (elementIndex === elementCount) {
+                    ++instanceIndex
+                    isNextNewInstance = true
+                    if (instanceIndex < instanceCount) elementIndex = 0
+                } else {
+                    isNextNewInstance = false
+                }
+                hasNext = elementIndex < elementCount
+            }
+            return value
+        },
+        skipInstance() {
+            if (hasNext && value.instanceIndex === instanceIndex) {
+                ++instanceIndex
+                elementIndex = 0
+                hasNext = instanceIndex < instanceCount
+            }
+        }
+    }
+}
+
+export namespace StructureElementIterator {
+    export function fromGroup(group: Unit.SymmetryGroup): LocationIterator {
+        const unit = group.units[0]
+        const elementCount = group.elements.length
+        const instanceCount = group.units.length
+        const location = StructureElement.create(unit)
+        const getLocation = (elementIndex: number, instanceIndex: number) => {
+            location.element = unit.elements[elementIndex]
+            return location
+        }
+        return LocationIterator(elementCount, instanceCount, getLocation)
+    }
+}
+
+export namespace LinkIterator {
+    export function fromGroup(group: Unit.SymmetryGroup): LocationIterator {
+        const unit = group.units[0]
+        const elementCount = Unit.isAtomic(unit) ? unit.links.edgeCount * 2 : 0
+        const instanceCount = group.units.length
+        const location = StructureElement.create(unit)
+        const getLocation = (elementIndex: number, instanceIndex: number) => {
+            location.element = unit.elements[(unit as Unit.Atomic).links.a[elementIndex]]
+            return location
+        }
+        return LocationIterator(elementCount, instanceCount, getLocation)
+    }
+
+    export function fromStructure(structure: Structure): LocationIterator {
+        const elementCount = structure.links.bondCount
+        const instanceCount = 1
+        const location = Link.Location()
+        const getLocation = (elementIndex: number, instanceIndex: number) => {
+            const bond = structure.links.bonds[elementIndex]
+            location.aUnit = bond.unitA
+            location.aIndex = bond.indexA as StructureElement.UnitIndex
+            location.bUnit = bond.unitB
+            location.bIndex = bond.indexB as StructureElement.UnitIndex
+            return location
+        }
+        return LocationIterator(elementCount, instanceCount, getLocation)
+    }
+}
\ No newline at end of file
diff --git a/src/mol-geo/representation/volume/surface.ts b/src/mol-geo/representation/volume/surface.ts
index dca3c980d53cc71879a91e54b68faa81777c90df..3fcd4b89cf3b7e75591675b3037f43da5c0132ba 100644
--- a/src/mol-geo/representation/volume/surface.ts
+++ b/src/mol-geo/representation/volume/surface.ts
@@ -13,7 +13,7 @@ import { VolumeVisual } from '.';
 import { createMeshRenderObject, MeshRenderObject } from 'mol-gl/render-object';
 import { ValueCell, defaults } from 'mol-util';
 import { Mat4 } from 'mol-math/linear-algebra';
-import { createUniformColor } from '../../util/color-data';
+import { createValueColor } from '../../util/color-data';
 import { getMeshData } from '../../util/mesh-data';
 import { RenderableState, MeshValues } from 'mol-gl/renderable';
 import { PickingId } from '../../util/picking';
@@ -65,7 +65,7 @@ export default function SurfaceVisual(): VolumeVisual<SurfaceProps> {
             }
 
             const instanceCount = 1
-            const color = createUniformColor({ value: 0x7ec0ee })
+            const color = createValueColor(0x7ec0ee)
             const marker = createEmptyMarkers()
 
             const values: MeshValues = {
diff --git a/src/mol-geo/theme/structure/color/chain-id.ts b/src/mol-geo/theme/structure/color/chain-id.ts
index d703aa303e3822fbb65794ea4db9a842a8a1516c..c0bc8e83f658e0e53e4d729befceb1cf550bb146 100644
--- a/src/mol-geo/theme/structure/color/chain-id.ts
+++ b/src/mol-geo/theme/structure/color/chain-id.ts
@@ -4,11 +4,11 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Unit, StructureProperties, StructureElement } from 'mol-model/structure';
+import { Unit, StructureProperties, StructureElement, Link } from 'mol-model/structure';
 
-import { StructureColorDataProps } from '.';
-import { ColorData, createElementColor, createUniformColor } from '../../../util/color-data';
-import { ColorScale } from 'mol-util/color';
+import { ColorData, createElementColor } from '../../../util/color-data';
+import { ColorScale, Color } from 'mol-util/color';
+import { LocationIterator, LocationValue } from '../../../representation/structure/visual/util/location-iterator';
 
 function getAsymId(unit: Unit): StructureElement.Property<string> {
     switch (unit.kind) {
@@ -18,54 +18,28 @@ function getAsymId(unit: Unit): StructureElement.Property<string> {
         case Unit.Kind.Gaussians:
             return StructureProperties.coarse.asym_id
     }
-    throw new Error('unhandled unit kind')
 }
 
-export function chainIdColorData(props: StructureColorDataProps, locationFn: (l: StructureElement, renderElementIdx: number) => void, colorData?: ColorData) {
-    const { group: { units }, elementCount } = props
-    const unit = units[0]
-
-    const map = unit.model.properties.asymIdSerialMap
-    const count = map.size
-
-    const domain = [ 0, count - 1 ]
-    const scale = ColorScale.create({ domain })
-    const asym_id = getAsymId(unit)
-
+export function chainIdColorData(locationIt: LocationIterator, colorData?: ColorData) {
     const l = StructureElement.create()
-    l.unit = unit
 
-    return createElementColor({
-        colorFn: (renderElementIdx: number) => {
-            locationFn(l, renderElementIdx)
+    function colorFn(locationValue: LocationValue): Color {
+        const { location } = locationValue
+        if (StructureElement.isLocation(location)) {
+            const map = location.unit.model.properties.asymIdSerialMap
+            const scale = ColorScale.create({ domain: [ 0, map.size - 1 ] })
+            const asym_id = getAsymId(location.unit)
+            return scale.color(map.get(asym_id(location)) || 0)
+        } else if (Link.isLocation(location)) {
+            const map = location.aUnit.model.properties.asymIdSerialMap
+            const scale = ColorScale.create({ domain: [ 0, map.size - 1 ] })
+            const asym_id = getAsymId(location.aUnit)
+            l.unit = location.aUnit
+            l.element = location.aUnit.elements[location.aIndex]
             return scale.color(map.get(asym_id(l)) || 0)
-        },
-        elementCount
-    }, colorData)
-}
-
-export function chainIdElementColorData(props: StructureColorDataProps, colorData?: ColorData) {
-    const elements = props.group.units[0].elements
-    function locationFn(l: StructureElement, renderElementIdx: number) {
-        l.element = elements[renderElementIdx]
+        }
+        return 0
     }
-    return chainIdColorData(props, locationFn, colorData)
-}
 
-export function chainIdLinkColorData(props: StructureColorDataProps, colorData?: ColorData): ColorData {
-    const unit = props.group.units[0]
-    const elements = unit.elements
-    let locationFn: (l: StructureElement, renderElementIdx: number) => void
-    switch (unit.kind) {
-        case Unit.Kind.Atomic:
-            const { a } = unit.links
-            locationFn = (l: StructureElement, renderElementIdx: number) => {
-                l.element = elements[a[renderElementIdx]]
-            }
-            return chainIdColorData(props, locationFn, colorData)
-        case Unit.Kind.Spheres:
-        case Unit.Kind.Gaussians:
-            // no chainId link color for coarse units, return uniform grey color
-            return createUniformColor({ value: 0xCCCCCC }, colorData)
-    }
+    return createElementColor(locationIt, colorFn, colorData)
 }
\ No newline at end of file
diff --git a/src/mol-geo/theme/structure/color/element-index.ts b/src/mol-geo/theme/structure/color/element-index.ts
index 510e0a4d54da27aef3546f973eb222239a486492..7cd06ef40a7be1b70c7e1379e43946fa612becc2 100644
--- a/src/mol-geo/theme/structure/color/element-index.ts
+++ b/src/mol-geo/theme/structure/color/element-index.ts
@@ -5,18 +5,17 @@
  */
 
 import { ColorScale } from 'mol-util/color';
-import { StructureColorDataProps } from '.';
 import { createElementInstanceColor, ColorData } from '../../../util/color-data';
+import { LocationIterator, LocationValue } from '../../../representation/structure/visual/util/location-iterator';
 
-export function elementIndexColorData(props: StructureColorDataProps, colorData?: ColorData) {
-    const { group: { units }, elementCount } = props
-    const instanceCount = units.length
+export function elementIndexColorData(locationIt: LocationIterator, colorData?: ColorData) {
+    const { elementCount, instanceCount } = locationIt
 
     const domain = [ 0, instanceCount * elementCount - 1 ]
     const scale = ColorScale.create({ domain })
-    return createElementInstanceColor({
-        colorFn: (instanceIdx, elementIdx) => scale.color(instanceIdx * elementCount + elementIdx),
-        instanceCount,
-        elementCount
-    }, colorData)
+    return createElementInstanceColor(
+        locationIt,
+        (value: LocationValue) => scale.color(value.instanceIndex * elementCount + value.elementIndex),
+        colorData
+    )
 }
\ No newline at end of file
diff --git a/src/mol-geo/theme/structure/color/element-symbol.ts b/src/mol-geo/theme/structure/color/element-symbol.ts
index eebb514d9320bbbbea90b5701986abb2185edc84..5c99412f67a9faeba1d2f81214e900b413360f04 100644
--- a/src/mol-geo/theme/structure/color/element-symbol.ts
+++ b/src/mol-geo/theme/structure/color/element-symbol.ts
@@ -6,8 +6,9 @@
 
 import { ElementSymbol } from 'mol-model/structure/model/types';
 import { Color } from 'mol-util/color';
-import { StructureColorDataProps } from '.';
 import { createElementColor, ColorData } from '../../../util/color-data';
+import { StructureElement, Unit, Link } from 'mol-model/structure';
+import { LocationIterator, LocationValue } from '../../../representation/structure/visual/util/location-iterator';
 
 // from Jmol http://jmol.sourceforge.net/jscolors/ (or 0xFFFFFF)
 export const ElementSymbolColors: { [k: string]: Color } = {
@@ -21,14 +22,22 @@ export function elementSymbolColor(element: ElementSymbol): Color {
     return c === void 0 ? DefaultElementSymbolColor : c
 }
 
-export function elementSymbolColorData(props: StructureColorDataProps, colorData?: ColorData) {
-    const { group: { units, elements }, elementCount } = props
-    const { type_symbol } = units[0].model.atomicHierarchy.atoms
-    return createElementColor({
-        colorFn: (elementIdx: number) => {
-            const e = elements[elementIdx]
-            return elementSymbolColor(type_symbol.value(e))
-        },
-        elementCount
-    }, colorData)
+export function elementSymbolColorData(locationIt: LocationIterator, colorData?: ColorData) {
+    function colorFn(locationValue: LocationValue): Color {
+        const { location } = locationValue
+        if (StructureElement.isLocation(location)) {
+            if (Unit.isAtomic(location.unit)) {
+                const { type_symbol } = location.unit.model.atomicHierarchy.atoms
+                return elementSymbolColor(type_symbol.value(location.element))
+            }
+        } else if (Link.isLocation(location)) {
+            if (Unit.isAtomic(location.aUnit)) {
+                const { type_symbol } = location.aUnit.model.atomicHierarchy.atoms
+                return elementSymbolColor(type_symbol.value(location.aUnit.elements[location.aIndex]))
+            }
+        }
+        return DefaultElementSymbolColor
+    }
+
+    return createElementColor(locationIt, colorFn, colorData)
 }
\ No newline at end of file
diff --git a/src/mol-geo/theme/structure/color/index.ts b/src/mol-geo/theme/structure/color/index.ts
index 41645f2764500d172bf531be61e8a8315743f746..05e8c32000a48bc98be0563577e957dbeb76b3e3 100644
--- a/src/mol-geo/theme/structure/color/index.ts
+++ b/src/mol-geo/theme/structure/color/index.ts
@@ -12,6 +12,6 @@ export interface StructureColorDataProps {
 }
 
 export { elementIndexColorData } from './element-index'
-export { chainIdElementColorData } from './chain-id'
+export { chainIdColorData } from './chain-id'
 export { elementSymbolColorData } from './element-symbol'
 export { instanceIndexColorData } from './instance-index'
\ No newline at end of file
diff --git a/src/mol-geo/theme/structure/color/instance-index.ts b/src/mol-geo/theme/structure/color/instance-index.ts
index bc79c2c22fc4dbab17f7932ef48d38f5fc884548..ca40a1d004ede661b4e5d46a04150cf709e77c35 100644
--- a/src/mol-geo/theme/structure/color/instance-index.ts
+++ b/src/mol-geo/theme/structure/color/instance-index.ts
@@ -5,17 +5,17 @@
  */
 
 import { ColorScale } from 'mol-util/color';
-import { StructureColorDataProps } from '.';
 import { createInstanceColor, ColorData } from '../../../util/color-data';
+import { LocationIterator, LocationValue } from '../../../representation/structure/visual/util/location-iterator';
 
-export function instanceIndexColorData(props: StructureColorDataProps, colorData?: ColorData) {
-    const { group: { units } } = props
-    const instanceCount = units.length
-
+export function instanceIndexColorData(locationIt: LocationIterator, colorData?: ColorData) {
+    const { instanceCount } = locationIt
     const domain = [ 0, instanceCount - 1 ]
     const scale = ColorScale.create({ domain })
-    return createInstanceColor({
-        colorFn: scale.color,
-        instanceCount
-    }, colorData)
+
+    function colorFn(locationValue: LocationValue) {
+        return scale.color(locationValue.instanceIndex)
+    }
+
+    return createInstanceColor(locationIt, colorFn, colorData)
 }
\ No newline at end of file
diff --git a/src/mol-geo/util/color-data.ts b/src/mol-geo/util/color-data.ts
index 4aa5698518d6960ebcd630bf240981a73713d854..7b207d8db6338e46595ae033c1e9c17a570401d3 100644
--- a/src/mol-geo/util/color-data.ts
+++ b/src/mol-geo/util/color-data.ts
@@ -7,8 +7,8 @@
 import { ValueCell } from 'mol-util';
 import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
 import { Color } from 'mol-util/color';
-import VertexMap from '../shape/vertex-map';
 import { Vec2, Vec3 } from 'mol-math/linear-algebra';
+import { LocationIterator, LocationValue, NullLocationValue } from '../representation/structure/visual/util/location-iterator';
 
 export type ColorType = 'uniform' | 'attribute' | 'instance' | 'element' | 'elementInstance'
 
@@ -20,6 +20,8 @@ export type ColorData = {
     dColorType: ValueCell<string>,
 }
 
+export type LocationColor = (locationValue: LocationValue) => Color
+
 const emptyColorTexture = { array: new Uint8Array(3), width: 1, height: 1 }
 function createEmptyColorTexture() {
     return {
@@ -28,21 +30,16 @@ function createEmptyColorTexture() {
     }
 }
 
-export interface UniformColorProps {
-    value: Color
-}
-
-/** Creates color uniform */
-export function createUniformColor(props: UniformColorProps, colorData?: ColorData): ColorData {
+export function createValueColor(value: Color, colorData?: ColorData): ColorData {
     if (colorData) {
-        ValueCell.update(colorData.uColor, Color.toRgbNormalized(props.value) as Vec3)
+        ValueCell.update(colorData.uColor, Color.toRgbNormalized(value) as Vec3)
         if (colorData.dColorType.ref.value !== 'uniform') {
             ValueCell.update(colorData.dColorType, 'uniform')
         }
         return colorData
     } else {
         return {
-            uColor: ValueCell.create(Color.toRgbNormalized(props.value) as Vec3),
+            uColor: ValueCell.create(Color.toRgbNormalized(value) as Vec3),
             aColor: ValueCell.create(new Float32Array(0)),
             ...createEmptyColorTexture(),
             dColorType: ValueCell.create('uniform'),
@@ -50,39 +47,44 @@ export function createUniformColor(props: UniformColorProps, colorData?: ColorDa
     }
 }
 
-export interface AttributeColorProps {
-    colorFn: (elementIdx: number) => Color
-    vertexMap: VertexMap
+/** Creates color uniform */
+export function createUniformColor(locationIt: LocationIterator, colorFn: LocationColor, colorData?: ColorData): ColorData {
+    return createValueColor(colorFn(NullLocationValue), colorData)
 }
 
-/** Creates color attribute with color for each element (i.e. shared across instances/units) */
-export function createAttributeColor(props: AttributeColorProps, colorData?: ColorData): ColorData {
-    const { colorFn, vertexMap } = props
-    const { idCount, offsetCount, offsets } = vertexMap
-    const colors = new Float32Array(idCount * 3);
-    for (let i = 0, il = offsetCount - 1; i < il; ++i) {
-        const start = offsets[i]
-        const end = offsets[i + 1]
-        const hexColor = colorFn(i)
-        for (let i = start, il = end; i < il; ++i) {
-            Color.toArrayNormalized(hexColor, colors, i * 3)
-        }
-    }
-    if (colorData) {
-        ValueCell.update(colorData.aColor, colors)
-        if (colorData.dColorType.ref.value !== 'attribute') {
-            ValueCell.update(colorData.dColorType, 'attribute')
-        }
-        return colorData
-    } else {
-        return {
-            uColor: ValueCell.create(Vec3.zero()),
-            aColor: ValueCell.create(colors),
-            ...createEmptyColorTexture(),
-            dColorType: ValueCell.create('attribute'),
-        }
-    }
-}
+// export interface AttributeColorProps {
+//     colorFn: (elementIdx: number) => Color
+//     vertexMap: VertexMap
+// }
+
+// /** Creates color attribute with color for each element (i.e. shared across instances/units) */
+// export function createAttributeColor(props: AttributeColorProps, colorData?: ColorData): ColorData {
+//     const { colorFn, vertexMap } = props
+//     const { idCount, offsetCount, offsets } = vertexMap
+//     const colors = new Float32Array(idCount * 3);
+//     for (let i = 0, il = offsetCount - 1; i < il; ++i) {
+//         const start = offsets[i]
+//         const end = offsets[i + 1]
+//         const hexColor = colorFn(i)
+//         for (let i = start, il = end; i < il; ++i) {
+//             Color.toArrayNormalized(hexColor, colors, i * 3)
+//         }
+//     }
+//     if (colorData) {
+//         ValueCell.update(colorData.aColor, colors)
+//         if (colorData.dColorType.ref.value !== 'attribute') {
+//             ValueCell.update(colorData.dColorType, 'attribute')
+//         }
+//         return colorData
+//     } else {
+//         return {
+//             uColor: ValueCell.create(Vec3.zero()),
+//             aColor: ValueCell.create(colors),
+//             ...createEmptyColorTexture(),
+//             dColorType: ValueCell.create('attribute'),
+//         }
+//     }
+// }
 
 export function createTextureColor(colors: TextureImage, type: ColorType, colorData?: ColorData): ColorData {
     if (colorData) {
@@ -103,53 +105,38 @@ export function createTextureColor(colors: TextureImage, type: ColorType, colorD
     }
 }
 
-export interface InstanceColorProps {
-    colorFn: (instanceIdx: number) => Color
-    instanceCount: number
-}
-
 /** Creates color texture with color for each instance/unit */
-export function createInstanceColor(props: InstanceColorProps, colorData?: ColorData): ColorData {
-    const { colorFn, instanceCount} = props
+export function createInstanceColor(locationIt: LocationIterator, colorFn: LocationColor, colorData?: ColorData): ColorData {
+    const { instanceCount} = locationIt
     const colors = colorData && colorData.tColor.ref.value.array.length >= instanceCount * 3 ? colorData.tColor.ref.value : createTextureImage(instanceCount, 3)
-    for (let i = 0; i < instanceCount; i++) {
-        Color.toArray(colorFn(i), colors.array, i * 3)
+    while (locationIt.hasNext && !locationIt.isNextNewInstance) {
+        const value = locationIt.move()
+        Color.toArray(colorFn(value), colors.array, value.index * 3)
+        locationIt.skipInstance()
     }
     return createTextureColor(colors, 'instance', colorData)
 }
 
-export interface ElementColorProps {
-    colorFn: (elementIdx: number) => Color
-    elementCount: number
-}
-
 /** Creates color texture with color for each element (i.e. shared across instances/units) */
-export function createElementColor(props: ElementColorProps, colorData?: ColorData): ColorData {
-    const { colorFn, elementCount } = props
+export function createElementColor(locationIt: LocationIterator, colorFn: LocationColor, colorData?: ColorData): ColorData {
+    const { elementCount } = locationIt
     const colors = colorData && colorData.tColor.ref.value.array.length >= elementCount * 3 ? colorData.tColor.ref.value : createTextureImage(elementCount, 3)
-    for (let i = 0, il = elementCount; i < il; ++i) {
-        Color.toArray(colorFn(i), colors.array, i * 3)
+    while (locationIt.hasNext && !locationIt.isNextNewInstance) {
+        const value = locationIt.move()
+        // console.log(value)
+        Color.toArray(colorFn(value), colors.array, value.elementIndex * 3)
     }
     return createTextureColor(colors, 'element', colorData)
 }
 
-export interface ElementInstanceColorProps {
-    colorFn: (instanceIdx: number, elementIdx: number) => Color
-    instanceCount: number,
-    elementCount: number
-}
-
 /** Creates color texture with color for each element instance (i.e. for each unit) */
-export function createElementInstanceColor(props: ElementInstanceColorProps, colorData?: ColorData): ColorData {
-    const { colorFn, instanceCount, elementCount } = props
+export function createElementInstanceColor(locationIt: LocationIterator, colorFn: LocationColor, colorData?: ColorData): ColorData {
+    const { elementCount, instanceCount } = locationIt
     const count = instanceCount * elementCount
     const colors = colorData && colorData.tColor.ref.value.array.length >= count * 3 ? colorData.tColor.ref.value : createTextureImage(count, 3)
-    let colorOffset = 0
-    for (let i = 0; i < instanceCount; i++) {
-        for (let j = 0, jl = elementCount; j < jl; ++j) {
-            Color.toArray(colorFn(i, j), colors.array, colorOffset)
-            colorOffset += 3
-        }
+    while (locationIt.hasNext && !locationIt.isNextNewInstance) {
+        const value = locationIt.move()
+        Color.toArray(colorFn(value), colors.array, value.index * 3)
     }
     return createTextureColor(colors, 'elementInstance', colorData)
 }
\ No newline at end of file
diff --git a/src/mol-gl/_spec/renderer.spec.ts b/src/mol-gl/_spec/renderer.spec.ts
index 52e7cfe073dc36d4b6796c1b5bfb098fa9c5c14f..c314659a2856b9c88eb0577b6168503672dc94a1 100644
--- a/src/mol-gl/_spec/renderer.spec.ts
+++ b/src/mol-gl/_spec/renderer.spec.ts
@@ -11,7 +11,7 @@ import { Vec3, Mat4 } from 'mol-math/linear-algebra';
 import { ValueCell } from 'mol-util';
 
 import Renderer from '../renderer';
-import { createUniformColor } from 'mol-geo/util/color-data';
+import { createValueColor } from 'mol-geo/util/color-data';
 import { createUniformSize } from 'mol-geo/util/size-data';
 import { createContext } from '../webgl/context';
 import { RenderableState } from '../renderable';
@@ -47,7 +47,7 @@ function createPoints() {
     const aPosition = ValueCell.create(new Float32Array([0, -1, 0, -1, 0, 0, 1, 1, 0]))
     const aElementId = ValueCell.create(fillSerial(new Float32Array(3)))
     const aInstanceId = ValueCell.create(fillSerial(new Float32Array(1)))
-    const color = createUniformColor({ value: 0xFF0000 })
+    const color = createValueColor(0xFF0000)
     const size = createUniformSize({ value: 1 })
     const marker = createEmptyMarkers()
 
diff --git a/src/mol-model/location.ts b/src/mol-model/location.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c3361122aa7839d48c84b000aaf3cc13bb1608d7
--- /dev/null
+++ b/src/mol-model/location.ts
@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { StructureElement } from './structure'
+import { Link } from './structure/structure/unit/links'
+
+/** A null value Location */
+export const NullLocation = { kind: 'null-location' as 'null-location' }
+export type NullLocation = typeof NullLocation
+export function isNullLocation(x: any): x is NullLocation {
+    return !!x && x.kind === 'null-location';
+}
+
+export type Location = StructureElement | Link.Location | NullLocation
\ No newline at end of file
diff --git a/src/mol-model/loci.ts b/src/mol-model/loci.ts
index c235548f98a8905ede4371d801d7fe25b17decdc..37a679c9c9ef3f856f0b40d8e246714f83dadd25 100644
--- a/src/mol-model/loci.ts
+++ b/src/mol-model/loci.ts
@@ -14,7 +14,7 @@ export function isEveryLoci(x: any): x is EveryLoci {
     return !!x && x.kind === 'every-loci';
 }
 
-/** A Loci that that is empty */
+/** A Loci that is empty */
 export const EmptyLoci = { kind: 'empty-loci' as 'empty-loci' }
 export type EmptyLoci = typeof EmptyLoci
 export function isEmptyLoci(x: any): x is EmptyLoci {
diff --git a/src/mol-model/structure/structure/element.ts b/src/mol-model/structure/structure/element.ts
index f8e35caff577b023e4d64ef5c19abf1822ac3bc3..cae429e50aacb4eaf637cbb3676c1248e1efe9ab 100644
--- a/src/mol-model/structure/structure/element.ts
+++ b/src/mol-model/structure/structure/element.ts
@@ -9,13 +9,16 @@ import Unit from './unit'
 import { ElementIndex } from '../model';
 
 interface StructureElement<U = Unit> {
+    readonly kind: 'element-location',
     unit: U,
     /** Index into element (atomic/coarse) properties of unit.model */
     element: ElementIndex
 }
 
 namespace StructureElement {
-    export function create(unit?: Unit, element?: ElementIndex): StructureElement { return { unit: unit as any, element: element || (0 as ElementIndex) }; }
+    export function create(unit?: Unit, element?: ElementIndex): StructureElement {
+        return { kind: 'element-location', unit: unit as any, element: element || (0 as ElementIndex) };
+    }
 
     // TODO: when nominal types are available, make this indexed by UnitIndex
     export type Set = SortedArray<ElementIndex>
@@ -58,6 +61,10 @@ namespace StructureElement {
     export function isLoci(x: any): x is Loci {
         return !!x && x.kind === 'element-loci';
     }
+
+    export function isLocation(x: any): x is StructureElement {
+        return !!x && x.kind === 'element-location';
+    }
 }
 
 export default StructureElement
\ No newline at end of file
diff --git a/src/mol-model/structure/structure/unit/links.ts b/src/mol-model/structure/structure/unit/links.ts
index 20345c1bfd56e36caa39d01afe9d72ec38bc0aec..c7ff8e0a7399f3d4f2fc243803846dfd29fa259f 100644
--- a/src/mol-model/structure/structure/unit/links.ts
+++ b/src/mol-model/structure/structure/unit/links.ts
@@ -1,10 +1,11 @@
 /**
- * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2018 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 { Unit } from '../../structure'
+import { Unit, StructureElement } from '../../structure'
 
 export * from './links/data'
 export * from './links/intra-compute'
@@ -12,12 +13,21 @@ export * from './links/inter-compute'
 
 namespace Link {
     export interface Location {
-        readonly aUnit: Unit,
+        readonly kind: 'link-location',
+        aUnit: Unit,
         /** Index into aUnit.elements */
-        readonly aIndex: number,
-        readonly bUnit: Unit,
+        aIndex: StructureElement.UnitIndex,
+        bUnit: Unit,
         /** Index into bUnit.elements */
-        readonly bIndex: number,
+        bIndex: StructureElement.UnitIndex,
+    }
+
+    export function Location(aUnit?: Unit, aIndex?: StructureElement.UnitIndex, bUnit?: Unit, bIndex?: StructureElement.UnitIndex): Location {
+        return { kind: 'link-location', aUnit: aUnit as any, aIndex: aIndex as any, bUnit: bUnit as any, bIndex: bIndex as any };
+    }
+
+    export function isLocation(x: any): x is Location {
+        return !!x && x.kind === 'link-location';
     }
 
     export interface Loci {
diff --git a/src/mol-view/stage.ts b/src/mol-view/stage.ts
index 427399122897cd29644f337f79f5f445cb2f4d24..c88e165ecbd419f523053e38b8743c9fa9bbfdfc 100644
--- a/src/mol-view/stage.ts
+++ b/src/mol-view/stage.ts
@@ -78,7 +78,7 @@ export class Stage {
         // this.loadPdbid('1hrv') // viral assembly
         // this.loadPdbid('1rb8') // virus
         // this.loadPdbid('1blu') // metal coordination
-        // this.loadPdbid('3pqr') // inter unit bonds, two polymer chains, ligands, water, carbohydrates linked to protein
+        this.loadPdbid('3pqr') // inter unit bonds, two polymer chains, ligands, water, carbohydrates linked to protein
         // this.loadPdbid('4v5a') // ribosome
         // this.loadPdbid('3j3q') // ...
         // this.loadPdbid('2np2') // dna
@@ -105,7 +105,7 @@ export class Stage {
         // this.loadPdbid('2fnc') // contains maltotriose
         // this.loadPdbid('4zs9') // contains raffinose
         // this.loadPdbid('2yft') // contains kestose
-        this.loadPdbid('2b5t') // contains large carbohydrate polymer
+        // this.loadPdbid('2b5t') // contains large carbohydrate polymer
         // this.loadPdbid('1b5f') // contains carbohydrate with alternate locations
         // this.loadMmcifUrl(`../../examples/1cbs_full.bcif`)
         // this.loadMmcifUrl(`../../examples/1cbs_updated.cif`)