diff --git a/src/mol-repr/structure/representation/ball-and-stick.ts b/src/mol-repr/structure/representation/ball-and-stick.ts
index d28d478975ce8b18f360dfbbf783ce2b89815bb3..4ea997482b639aaa597c14fc2674ebf4200816ef 100644
--- a/src/mol-repr/structure/representation/ball-and-stick.ts
+++ b/src/mol-repr/structure/representation/ball-and-stick.ts
@@ -30,7 +30,8 @@ export const BallAndStickParams = {
     ...IntraUnitLinkParams,
     ...InterUnitLinkParams,
     unitKinds: PD.MultiSelect<UnitKind>(['atomic'], UnitKindOptions),
-    sizeFactor: PD.Numeric(0.2, { min: 0.01, max: 10, step: 0.01 }),
+    sizeFactor: PD.Numeric(0.3, { min: 0.01, max: 10, step: 0.01 }),
+    sizeAspectRatio: PD.Numeric(2/3, { min: 0.01, max: 3, step: 0.01 }),
     colorTheme: PD.Mapped('polymer-index', BuiltInColorThemeOptions, name => PD.Group((BuiltInColorThemes as { [k: string]: ColorTheme.Provider<any> })[name].getParams({}))),
     visuals: PD.MultiSelect<BallAndStickVisualName>(['element-sphere', 'intra-link', 'inter-link'], BallAndStickVisualOptions),
 }
diff --git a/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts b/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
index 05d5a04b0d66afd2c4bf649fb66826835538fe13..a85dddd51386a2f05e1554529d4363533f48789d 100644
--- a/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
@@ -51,7 +51,6 @@ async function createCarbohydrateLinkCylinderMesh(ctx: VisualContext, structure:
 export const CarbohydrateLinkParams = {
     ...UnitsMeshParams,
     ...LinkCylinderParams,
-    detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }),
     linkSizeFactor: PD.Numeric(0.3, { min: 0, max: 3, step: 0.01 }),
 }
 export type CarbohydrateLinkParams = typeof CarbohydrateLinkParams
@@ -64,7 +63,10 @@ export function CarbohydrateLinkVisual(): ComplexVisual<CarbohydrateLinkParams>
         getLoci: getLinkLoci,
         mark: markLink,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<CarbohydrateLinkParams>, currentProps: PD.Values<CarbohydrateLinkParams>) => {
-            state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
+            state.createGeometry = (
+                newProps.linkSizeFactor !== currentProps.linkSizeFactor ||
+                newProps.radialSegments !== currentProps.radialSegments
+            )
         }
     })
 }
diff --git a/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts b/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts
index 7a5acbf598fc3d6b1241632712d95c97ee30358e..9a42ca5f74712690b84fe5aa77a2ec001c660391 100644
--- a/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts
+++ b/src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts
@@ -159,7 +159,10 @@ export function CarbohydrateSymbolVisual(): ComplexVisual<CarbohydrateSymbolPara
         getLoci: getCarbohydrateLoci,
         mark: markCarbohydrate,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<CarbohydrateSymbolParams>, currentProps: PD.Values<CarbohydrateSymbolParams>) => {
-            state.createGeometry = newProps.detail !== currentProps.detail
+            state.createGeometry = (
+                newProps.sizeFactor !== currentProps.sizeFactor ||
+                newProps.detail !== currentProps.detail
+            )
         }
     })
 }
diff --git a/src/mol-repr/structure/visual/carbohydrate-terminal-link-cylinder.ts b/src/mol-repr/structure/visual/carbohydrate-terminal-link-cylinder.ts
index 7d594fd788a192d1fa5fb1df4e2ec6f2bff9ca65..3c4d57853b4140d8fba7d23ae49c39c39d42dd7c 100644
--- a/src/mol-repr/structure/visual/carbohydrate-terminal-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/carbohydrate-terminal-link-cylinder.ts
@@ -61,7 +61,6 @@ async function createCarbohydrateTerminalLinkCylinderMesh(ctx: VisualContext, st
 export const CarbohydrateTerminalLinkParams = {
     ...UnitsMeshParams,
     ...LinkCylinderParams,
-    detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }),
     linkSizeFactor: PD.Numeric(0.3, { min: 0, max: 3, step: 0.01 }),
 }
 export type CarbohydrateTerminalLinkParams = typeof CarbohydrateTerminalLinkParams
@@ -74,7 +73,10 @@ export function CarbohydrateTerminalLinkVisual(): ComplexVisual<CarbohydrateTerm
         getLoci: getTerminalLinkLoci,
         mark: markTerminalLink,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<CarbohydrateTerminalLinkParams>, currentProps: PD.Values<CarbohydrateTerminalLinkParams>) => {
-            state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
+            state.createGeometry = (
+                newProps.linkSizeFactor !== currentProps.linkSizeFactor ||
+                newProps.radialSegments !== currentProps.radialSegments
+            )
         }
     })
 }
diff --git a/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts b/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
index 1b1d06ea80011194fc9b8b3993a4cc0e8133f1c2..bd1733ea4df225bf7dfa264c64e4c7d518022804 100644
--- a/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
+++ b/src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
@@ -7,7 +7,7 @@
 import { Link, Structure, StructureElement } from 'mol-model/structure';
 import { ComplexVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
-import { LinkCylinderProps, createLinkCylinderMesh, LinkCylinderParams } from './util/link';
+import { createLinkCylinderMesh, LinkCylinderParams } from './util/link';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { ComplexMeshVisual, ComplexMeshParams } from '../complex-visual';
@@ -21,10 +21,11 @@ import { PickingId } from 'mol-geo/geometry/picking';
 import { VisualContext } from 'mol-repr/representation';
 import { Theme } from 'mol-theme/theme';
 
-async function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: LinkCylinderProps, mesh?: Mesh) {
+async function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<CrossLinkRestraintParams>, mesh?: Mesh) {
 
     const crossLinks = structure.crossLinkRestraints
     if (!crossLinks.count) return Mesh.createEmpty(mesh)
+    const { sizeFactor } = props
 
     const location = StructureElement.create()
 
@@ -43,7 +44,7 @@ async function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structur
             const b = crossLinks.pairs[edgeIndex]
             location.unit = b.unitA
             location.element = b.unitA.elements[b.indexA]
-            return theme.size.size(location)
+            return theme.size.size(location) * sizeFactor
         }
     }
 
@@ -53,6 +54,7 @@ async function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structur
 export const CrossLinkRestraintParams = {
     ...ComplexMeshParams,
     ...LinkCylinderParams,
+    sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
 }
 export type CrossLinkRestraintParams = typeof CrossLinkRestraintParams
 
@@ -64,7 +66,10 @@ export function CrossLinkRestraintVisual(): ComplexVisual<CrossLinkRestraintPara
         getLoci: getLinkLoci,
         mark: markLink,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<CrossLinkRestraintParams>, currentProps: PD.Values<CrossLinkRestraintParams>) => {
-            state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
+            state.createGeometry = (
+                newProps.sizeFactor !== currentProps.sizeFactor ||
+                newProps.radialSegments !== currentProps.radialSegments
+            )
         }
     })
 }
diff --git a/src/mol-repr/structure/visual/element-sphere.ts b/src/mol-repr/structure/visual/element-sphere.ts
index 8f80022307ce1439079883cc4d93486d565ea14a..50c2abc39217083513b82f5cd984b3b2ac853e11 100644
--- a/src/mol-repr/structure/visual/element-sphere.ts
+++ b/src/mol-repr/structure/visual/element-sphere.ts
@@ -28,7 +28,10 @@ export function ElementSphereVisual(): UnitsVisual<ElementSphereParams> {
         getLoci: getElementLoci,
         mark: markElement,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<ElementSphereParams>, currentProps: PD.Values<ElementSphereParams>) => {
-            state.createGeometry = newProps.detail !== currentProps.detail
+            state.createGeometry = (
+                newProps.sizeFactor !== currentProps.sizeFactor ||
+                newProps.detail !== currentProps.detail
+            )
         }
     })
 }
\ No newline at end of file
diff --git a/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts b/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
index f85233d3541260ded2037f1c37c814e17e00be24..4a04144b4ed8923c5c13e8e2cb02b4e3aedb12bb 100644
--- a/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/inter-unit-link-cylinder.ts
@@ -53,7 +53,8 @@ async function createInterUnitLinkCylinderMesh(ctx: VisualContext, structure: St
 export const InterUnitLinkParams = {
     ...ComplexMeshParams,
     ...LinkCylinderParams,
-    sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
+    sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
+    sizeAspectRatio: PD.Numeric(2/3, { min: 0, max: 3, step: 0.01 }),
 }
 export type InterUnitLinkParams = typeof InterUnitLinkParams
 
@@ -65,9 +66,13 @@ export function InterUnitLinkVisual(): ComplexVisual<InterUnitLinkParams> {
         getLoci: getLinkLoci,
         mark: markLink,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<InterUnitLinkParams>, currentProps: PD.Values<InterUnitLinkParams>) => {
-            if (newProps.linkScale !== currentProps.linkScale) state.createGeometry = true
-            if (newProps.linkSpacing !== currentProps.linkSpacing) state.createGeometry = true
-            if (newProps.radialSegments !== currentProps.radialSegments) state.createGeometry = true
+            state.createGeometry = (
+                newProps.sizeFactor !== currentProps.sizeFactor ||
+                newProps.sizeAspectRatio !== currentProps.sizeAspectRatio ||
+                newProps.radialSegments !== currentProps.radialSegments ||
+                newProps.linkScale !== currentProps.linkScale ||
+                newProps.linkSpacing !== currentProps.linkSpacing
+            )
         }
     })
 }
diff --git a/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts b/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
index 5efdb9cbd6eb869ddf4813994aa003e405b6b85f..0467f0d5c0d813cf02d32c2ca55368f540e882db 100644
--- a/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
+++ b/src/mol-repr/structure/visual/intra-unit-link-cylinder.ts
@@ -8,7 +8,7 @@
 import { Unit, Link, StructureElement, Structure } from 'mol-model/structure';
 import { UnitsVisual } from '../representation';
 import { VisualUpdateState } from '../../util';
-import { LinkCylinderProps, createLinkCylinderMesh, LinkIterator, LinkCylinderParams } from './util/link';
+import { createLinkCylinderMesh, LinkIterator, LinkCylinderParams } from './util/link';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { UnitsMeshVisual, UnitsMeshParams, StructureGroup } from '../units-visual';
@@ -29,7 +29,7 @@ async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, s
     const links = unit.links
     const { edgeCount, a, b, edgeProps, offset } = links
     const { order: _order, flags: _flags } = edgeProps
-    const { sizeFactor } = props
+    const { sizeFactor, sizeAspectRatio } = props
 
     if (!edgeCount) return Mesh.createEmpty(mesh)
 
@@ -57,7 +57,7 @@ async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, s
         flags: (edgeIndex: number) => BitFlags.create(_flags[edgeIndex]),
         radius: (edgeIndex: number) => {
             location.element = elements[a[edgeIndex]]
-            return theme.size.size(location) * sizeFactor
+            return theme.size.size(location) * sizeFactor * sizeAspectRatio
         }
     }
 
@@ -67,7 +67,8 @@ async function createIntraUnitLinkCylinderMesh(ctx: VisualContext, unit: Unit, s
 export const IntraUnitLinkParams = {
     ...UnitsMeshParams,
     ...LinkCylinderParams,
-    sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
+    sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
+    sizeAspectRatio: PD.Numeric(2/3, { min: 0, max: 3, step: 0.01 }),
 }
 export type IntraUnitLinkParams = typeof IntraUnitLinkParams
 
@@ -78,10 +79,14 @@ export function IntraUnitLinkVisual(): UnitsVisual<IntraUnitLinkParams> {
         createLocationIterator: LinkIterator.fromGroup,
         getLoci: getLinkLoci,
         mark: markLink,
-        setUpdateState: (state: VisualUpdateState, newProps: LinkCylinderProps, currentProps: LinkCylinderProps) => {
-            if (newProps.linkScale !== currentProps.linkScale) state.createGeometry = true
-            if (newProps.linkSpacing !== currentProps.linkSpacing) state.createGeometry = true
-            if (newProps.radialSegments !== currentProps.radialSegments) state.createGeometry = true
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<IntraUnitLinkParams>, currentProps: PD.Values<IntraUnitLinkParams>) => {
+            state.createGeometry = (
+                newProps.sizeFactor !== currentProps.sizeFactor ||
+                newProps.sizeAspectRatio !== currentProps.sizeAspectRatio ||
+                newProps.radialSegments !== currentProps.radialSegments ||
+                newProps.linkScale !== currentProps.linkScale ||
+                newProps.linkSpacing !== currentProps.linkSpacing
+            )
         }
     })
 }
diff --git a/src/mol-repr/structure/visual/nucleotide-block-mesh.ts b/src/mol-repr/structure/visual/nucleotide-block-mesh.ts
index e208a134b65a989b51c55554d008be367a750efd..8e5188611682aa9e0f533e9cde7da22311997263 100644
--- a/src/mol-repr/structure/visual/nucleotide-block-mesh.ts
+++ b/src/mol-repr/structure/visual/nucleotide-block-mesh.ts
@@ -19,6 +19,7 @@ import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { addCylinder } from 'mol-geo/geometry/mesh/builder/cylinder';
 import { VisualContext } from 'mol-repr/representation';
 import { Theme } from 'mol-theme/theme';
+import { VisualUpdateState } from 'mol-repr/util';
 
 const p1 = Vec3.zero()
 const p2 = Vec3.zero()
@@ -132,6 +133,10 @@ export function NucleotideBlockVisual(): UnitsVisual<NucleotideBlockParams> {
         createLocationIterator: NucleotideLocationIterator.fromGroup,
         getLoci: getNucleotideElementLoci,
         mark: markNucleotideElement,
-        setUpdateState: () => {}
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<NucleotideBlockParams>, currentProps: PD.Values<NucleotideBlockParams>) => {
+            state.createGeometry = (
+                newProps.sizeFactor !== currentProps.sizeFactor
+            )
+        }
     })
 }
\ No newline at end of file
diff --git a/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts b/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts
index 62777f8bc72ef32cdaf5ffde8a70d455e8e58f37..2e27f6e0a8c4b76c988e1a568f57bc9600684b04 100644
--- a/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts
+++ b/src/mol-repr/structure/visual/polymer-backbone-cylinder.ts
@@ -21,6 +21,7 @@ import { VisualContext } from 'mol-repr/representation';
 import { Theme } from 'mol-theme/theme';
 
 export const PolymerBackboneCylinderParams = {
+    sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
     radialSegments: PD.Numeric(16, { min: 3, max: 56, step: 1 }),
 }
 export const DefaultPolymerBackboneCylinderProps = PD.getDefaultValues(PolymerBackboneCylinderParams)
@@ -30,7 +31,7 @@ async function createPolymerBackboneCylinderMesh(ctx: VisualContext, unit: Unit,
     const polymerElementCount = unit.polymerElements.length
     if (!polymerElementCount) return Mesh.createEmpty(mesh)
 
-    const { radialSegments } = props
+    const { radialSegments, sizeFactor } = props
 
     const vertexCountEstimate = radialSegments * 2 * polymerElementCount * 2
     const builder = MeshBuilder.create(vertexCountEstimate, vertexCountEstimate / 10, mesh)
@@ -48,11 +49,11 @@ async function createPolymerBackboneCylinderMesh(ctx: VisualContext, unit: Unit,
         pos(centerA.element, pA)
         pos(centerB.element, pB)
 
-        cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerA)
+        cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerA) * sizeFactor
         builder.setGroup(OrderedSet.indexOf(elements, centerA.element))
         addCylinder(builder, pA, pB, 0.5, cylinderProps)
 
-        cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerB)
+        cylinderProps.radiusTop = cylinderProps.radiusBottom = theme.size.size(centerB) * sizeFactor
         builder.setGroup(OrderedSet.indexOf(elements, centerB.element))
         addCylinder(builder, pB, pA, 0.5, cylinderProps)
 
@@ -80,7 +81,10 @@ export function PolymerBackboneVisual(): UnitsVisual<PolymerBackboneParams> {
         getLoci: getElementLoci,
         mark: markElement,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<PolymerBackboneParams>, currentProps: PD.Values<PolymerBackboneParams>) => {
-            state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
+            state.createGeometry = (
+                newProps.sizeFactor !== currentProps.sizeFactor ||
+                newProps.radialSegments !== currentProps.radialSegments
+            )
         }
     })
 }
\ No newline at end of file
diff --git a/src/mol-repr/structure/visual/polymer-direction-wedge.ts b/src/mol-repr/structure/visual/polymer-direction-wedge.ts
index 852fdbda7b48daa363d518672e07f5dc0a5f33fb..786ba2992b77deaf50e6ac687e74c8b93096a9c8 100644
--- a/src/mol-repr/structure/visual/polymer-direction-wedge.ts
+++ b/src/mol-repr/structure/visual/polymer-direction-wedge.ts
@@ -16,6 +16,7 @@ import { Mesh } from 'mol-geo/geometry/mesh/mesh';
 import { MeshBuilder } from 'mol-geo/geometry/mesh/mesh-builder';
 import { VisualContext } from 'mol-repr/representation';
 import { Theme } from 'mol-theme/theme';
+import { VisualUpdateState } from 'mol-repr/util';
 
 const t = Mat4.identity()
 const sVec = Vec3.zero()
@@ -101,6 +102,10 @@ export function PolymerDirectionVisual(): UnitsVisual<PolymerDirectionParams> {
         createLocationIterator: PolymerLocationIterator.fromGroup,
         getLoci: getPolymerElementLoci,
         mark: markPolymerElement,
-        setUpdateState: () => {}
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<PolymerDirectionParams>, currentProps: PD.Values<PolymerDirectionParams>) => {
+            state.createGeometry = (
+                newProps.sizeFactor !== currentProps.sizeFactor
+            )
+        }
     })
 }
\ No newline at end of file
diff --git a/src/mol-repr/structure/visual/polymer-gap-cylinder.ts b/src/mol-repr/structure/visual/polymer-gap-cylinder.ts
index 7b74e19ece9981d237feb2da18a622333dc3b4f1..818837ce18e3b010c18a63bc2c356afc32ec7446 100644
--- a/src/mol-repr/structure/visual/polymer-gap-cylinder.ts
+++ b/src/mol-repr/structure/visual/polymer-gap-cylinder.ts
@@ -96,7 +96,10 @@ export function PolymerGapVisual(): UnitsVisual<PolymerGapParams> {
         getLoci: getPolymerGapElementLoci,
         mark: markPolymerGapElement,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<PolymerGapParams>, currentProps: PD.Values<PolymerGapParams>) => {
-            state.createGeometry = newProps.radialSegments !== currentProps.radialSegments
+            state.createGeometry = (
+                newProps.sizeFactor !== currentProps.sizeFactor ||
+                newProps.radialSegments !== currentProps.radialSegments
+            )
         }
     })
 }
\ No newline at end of file
diff --git a/src/mol-repr/structure/visual/polymer-trace-mesh.ts b/src/mol-repr/structure/visual/polymer-trace-mesh.ts
index e4eaad776ba22cc17dcc2e9cadf02528aac1920c..65d6b070eacfdc1d0b933048b3da26af57fee671 100644
--- a/src/mol-repr/structure/visual/polymer-trace-mesh.ts
+++ b/src/mol-repr/structure/visual/polymer-trace-mesh.ts
@@ -101,6 +101,7 @@ export function PolymerTraceVisual(): UnitsVisual<PolymerTraceParams> {
         mark: markPolymerElement,
         setUpdateState: (state: VisualUpdateState, newProps: PD.Values<PolymerTraceParams>, currentProps: PD.Values<PolymerTraceParams>) => {
             state.createGeometry = (
+                newProps.sizeFactor !== currentProps.sizeFactor ||
                 newProps.linearSegments !== currentProps.linearSegments ||
                 newProps.radialSegments !== currentProps.radialSegments ||
                 newProps.aspectRatio !== currentProps.aspectRatio ||