diff --git a/src/mol-geo/geometry/mesh/builder/ellipsoid.ts b/src/mol-geo/geometry/mesh/builder/ellipsoid.ts
new file mode 100644
index 0000000000000000000000000000000000000000..453c9025c6430e2d6514cf1bc2e91ea8a64f7bda
--- /dev/null
+++ b/src/mol-geo/geometry/mesh/builder/ellipsoid.ts
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra';
+import { MeshBuilder } from '../mesh-builder';
+import { getSphere } from './sphere';
+
+const tmpEllipsoidMat = Mat4.identity()
+const tmpVec = Vec3()
+
+function setEllipsoidMat(m: Mat4, center: Vec3, dirMajor: Vec3, dirMinor: Vec3, radiusScale: Vec3) {
+    Vec3.add(tmpVec, center, dirMajor)
+    Mat4.targetTo(m, center, tmpVec, dirMinor)
+    Mat4.setTranslation(m, center)
+    return Mat4.scale(m, m, radiusScale)
+}
+
+export function addEllipsoid(state: MeshBuilder.State, center: Vec3, dirMajor: Vec3, dirMinor: Vec3, radiusScale: Vec3, detail: number) {
+    MeshBuilder.addPrimitive(state, setEllipsoidMat(tmpEllipsoidMat, center, dirMajor, dirMinor, radiusScale), getSphere(detail))
+}
\ No newline at end of file
diff --git a/src/mol-geo/geometry/mesh/builder/sphere.ts b/src/mol-geo/geometry/mesh/builder/sphere.ts
index 2427ed6cd994fcf1054a2835310437cabcacb0f6..041c62e5ff4f14ebf23f310faec76e847a76cf8e 100644
--- a/src/mol-geo/geometry/mesh/builder/sphere.ts
+++ b/src/mol-geo/geometry/mesh/builder/sphere.ts
@@ -16,7 +16,7 @@ function setSphereMat(m: Mat4, center: Vec3, radius: number) {
     return Mat4.scaleUniformly(m, Mat4.fromTranslation(m, center), radius)
 }
 
-function getSphere(detail: number) {
+export function getSphere(detail: number) {
     let sphere = sphereMap.get(detail)
     if (sphere === undefined) {
         sphere = Sphere(detail)
diff --git a/src/mol-model-formats/structure/mmcif/anisotropic.ts b/src/mol-model-formats/structure/mmcif/anisotropic.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dc6bf927665cd4d22203309cd9c60f6eef85e2e6
--- /dev/null
+++ b/src/mol-model-formats/structure/mmcif/anisotropic.ts
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Table } from '../../../mol-data/db';
+import { Model, CustomPropertyDescriptor } from '../../../mol-model/structure';
+import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
+import { CifWriter } from '../../../mol-io/writer/cif';
+
+export interface AtomSiteAnisotrop {
+    data: Table<AtomSiteAnisotrop.Schema['atom_site_anisotrop']>
+    /** maps atom_site-index to atom_site_anisotrop-index */
+    elementToAnsiotrop: Int32Array
+}
+
+export namespace AtomSiteAnisotrop {
+    export function getAtomSiteAnisotrop(model: Model) {
+        if (model.sourceData.kind !== 'mmCIF') return void 0;
+        const { atom_site_anisotrop } = model.sourceData.data
+        return Table.ofColumns(Schema.atom_site_anisotrop, atom_site_anisotrop);
+    }
+
+    export const PropName = '__AtomSiteAnisotrop__';
+    export function get(model: Model): AtomSiteAnisotrop | undefined {
+        if (model._staticPropertyData[PropName]) return model._staticPropertyData[PropName]
+        if (!model.customProperties.has(Descriptor)) return void 0;
+
+        const data = getAtomSiteAnisotrop(model);
+        if (!data) return void 0;
+
+        const prop = { data, elementToAnsiotrop: getElementToAnsiotrop(model, data) }
+        set(model, prop)
+
+        return prop;
+    }
+    function set(model: Model, prop: AtomSiteAnisotrop) {
+        (model._staticPropertyData[PropName] as AtomSiteAnisotrop) = prop;
+    }
+
+    export const Schema = { atom_site_anisotrop: mmCIF_Schema['atom_site_anisotrop'] };
+    export type Schema = typeof Schema
+
+    export const Descriptor: CustomPropertyDescriptor = {
+        isStatic: true,
+        name: 'atom_site_anisotrop',
+        cifExport: {
+            prefix: '',
+            categories: [{
+                name: 'atom_site_anisotrop',
+                instance(ctx) {
+                    const atom_site_anisotrop = getAtomSiteAnisotrop(ctx.firstModel);
+                    if (!atom_site_anisotrop) return CifWriter.Category.Empty;
+                    return CifWriter.Category.ofTable(atom_site_anisotrop);
+                }
+            }]
+        }
+    };
+
+    function getElementToAnsiotrop(model: Model, data: Table<Schema['atom_site_anisotrop']>) {
+        const { atomId } = model.atomicConformation
+        const atomIdToElement = new Int32Array(atomId.rowCount)
+        atomIdToElement.fill(-1)
+        for (let i = 0, il = atomId.rowCount; i < il; i++) {
+            atomIdToElement[atomId.value(i)] = i
+        }
+
+        const { id } = data
+        const elementToAnsiotrop = new Int32Array(atomId.rowCount)
+        elementToAnsiotrop.fill(-1)
+        for (let i = 0, il = id.rowCount; i < il; ++i) {
+            const ei = atomIdToElement[id.value(i)]
+            if (ei !== -1) elementToAnsiotrop[ei] = i
+        }
+
+        return elementToAnsiotrop
+    }
+
+    export async function attachFromMmCif(model: Model) {
+        if (model.customProperties.has(Descriptor)) return true;
+        if (model.sourceData.kind !== 'mmCIF') return false;
+        const atomSiteAnisotrop = getAtomSiteAnisotrop(model);
+        if (!atomSiteAnisotrop || atomSiteAnisotrop._rowCount === 0) return false;
+
+        model.customProperties.add(Descriptor);
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/src/mol-model-formats/structure/mmcif/parser.ts b/src/mol-model-formats/structure/mmcif/parser.ts
index e0945621e6789f60b8f53ee136e3761d6154ec11..12e6b99cf3552f3d7e90efb34aaed668f3616964 100644
--- a/src/mol-model-formats/structure/mmcif/parser.ts
+++ b/src/mol-model-formats/structure/mmcif/parser.ts
@@ -29,6 +29,7 @@ import { SaccharideComponentMap, SaccharideComponent, SaccharidesSnfgMap, Saccha
 import mmCIF_Format = ModelFormat.mmCIF
 import { memoize1 } from '../../../mol-util/memoize';
 import { ElementIndex, EntityIndex } from '../../../mol-model/structure/model';
+import { AtomSiteAnisotrop } from './anisotropic';
 
 export async function _parse_mmCif(format: mmCIF_Format, ctx: RuntimeContext) {
     const formatData = getFormatData(format)
@@ -279,6 +280,7 @@ function createModelIHM(format: mmCIF_Format, data: IHMData, formatData: FormatD
 function attachProps(model: Model) {
     ComponentBond.attachFromMmCif(model);
     StructConn.attachFromMmCif(model);
+    AtomSiteAnisotrop.attachFromMmCif(model);
 }
 
 function findModelEnd(num: Column<number>, startIndex: number) {
diff --git a/src/mol-repr/structure/registry.ts b/src/mol-repr/structure/registry.ts
index d2569805ea597e4eb2a000d7f0aa076b142bc99b..ba150d63a0ad3f1036a9aaa49cf2ad7ef26238b4 100644
--- a/src/mol-repr/structure/registry.ts
+++ b/src/mol-repr/structure/registry.ts
@@ -16,6 +16,7 @@ import { PointRepresentationProvider } from './representation/point';
 import { StructureRepresentationState } from './representation';
 import { PuttyRepresentationProvider } from './representation/putty';
 import { MolecularSurfaceRepresentationProvider } from './representation/molecular-surface';
+import { EllipsoidRepresentationProvider } from './representation/ellipsoid';
 
 export class StructureRepresentationRegistry extends RepresentationRegistry<Structure, StructureRepresentationState> {
     constructor() {
@@ -32,6 +33,7 @@ export const BuiltInStructureRepresentations = {
     'ball-and-stick': BallAndStickRepresentationProvider,
     'carbohydrate': CarbohydrateRepresentationProvider,
     'distance-restraint': DistanceRestraintRepresentationProvider,
+    'ellipsoid': EllipsoidRepresentationProvider,
     'gaussian-surface': GaussianSurfaceRepresentationProvider,
     // 'gaussian-volume': GaussianVolumeRepresentationProvider, // TODO disabled for now, needs more work
     'molecular-surface': MolecularSurfaceRepresentationProvider,
diff --git a/src/mol-repr/structure/representation/ellipsoid.ts b/src/mol-repr/structure/representation/ellipsoid.ts
new file mode 100644
index 0000000000000000000000000000000000000000..df74d6a297ad7407e5eb3b9658e713bd697082de
--- /dev/null
+++ b/src/mol-repr/structure/representation/ellipsoid.ts
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ParamDefinition as PD } from '../../../mol-util/param-definition';
+import { RepresentationParamsGetter, RepresentationContext, Representation } from '../../../mol-repr/representation';
+import { ThemeRegistryContext } from '../../../mol-theme/theme';
+import { Structure } from '../../../mol-model/structure';
+import { UnitsRepresentation, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationProvider } from '../../../mol-repr/structure/representation';
+import { EllipsoidMeshParams, EllipsoidMeshVisual } from '../visual/ellipsoid-mesh';
+import { UnitKind, UnitKindOptions } from '../../../mol-repr/structure/visual/util/common';
+import { AtomSiteAnisotrop } from '../../../mol-model-formats/structure/mmcif/anisotropic';
+
+const EllipsoidVisuals = {
+    'ellipsoid-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, EllipsoidParams>) => UnitsRepresentation('Ellipsoid Mesh', ctx, getParams, EllipsoidMeshVisual),
+}
+
+export const EllipsoidParams = {
+    ...EllipsoidMeshParams,
+    unitKinds: PD.MultiSelect<UnitKind>(['atomic'], UnitKindOptions),
+}
+export type EllipsoidParams = typeof EllipsoidMeshParams
+export function getEllipsoidParams(ctx: ThemeRegistryContext, structure: Structure) {
+    return PD.clone(EllipsoidMeshParams)
+}
+
+export type EllipsoidRepresentation = StructureRepresentation<EllipsoidParams>
+export function EllipsoidRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, EllipsoidParams>): EllipsoidRepresentation {
+    return Representation.createMulti('Ellipsoid', ctx, getParams, StructureRepresentationStateBuilder, EllipsoidVisuals as unknown as Representation.Def<Structure, EllipsoidParams>)
+}
+
+export const EllipsoidRepresentationProvider: StructureRepresentationProvider<EllipsoidParams> = {
+    label: 'Ellipsoid',
+    description: 'Displays anisotropic displacement ellipsoids of atomic elements.',
+    factory: EllipsoidRepresentation,
+    getParams: getEllipsoidParams,
+    defaultValues: PD.getDefaultValues(EllipsoidMeshParams),
+    defaultColorTheme: 'element-symbol',
+    defaultSizeTheme: 'uniform',
+    isApplicable: (structure: Structure) => structure.elementCount > 0 && structure.models.some(m => m.customProperties.has(AtomSiteAnisotrop.Descriptor))
+}
\ No newline at end of file
diff --git a/src/mol-repr/structure/visual/ellipsoid-mesh.ts b/src/mol-repr/structure/visual/ellipsoid-mesh.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4c3fc14d6cfde4aa96bc7c16b4531a46c6d1aa10
--- /dev/null
+++ b/src/mol-repr/structure/visual/ellipsoid-mesh.ts
@@ -0,0 +1,104 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ParamDefinition as PD } from '../../../mol-util/param-definition';
+import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual } from '../../../mol-repr/structure/units-visual';
+import { ElementIterator, getElementLoci, eachElement } from '../../../mol-repr/structure/visual/util/element';
+import { VisualUpdateState } from '../../../mol-repr/util';
+import { VisualContext } from '../../../mol-repr/visual';
+import { Unit, Structure, StructureElement } from '../../../mol-model/structure';
+import { Theme } from '../../../mol-theme/theme';
+import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
+import { sphereVertexCount } from '../../../mol-geo/primitive/sphere';
+import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
+import { Vec3, Mat3, Tensor } from '../../../mol-math/linear-algebra';
+import { isHydrogen } from '../../../mol-repr/structure/visual/util/common';
+import { addEllipsoid } from '../../../mol-geo/geometry/mesh/builder/ellipsoid';
+import { AtomSiteAnisotrop } from '../../../mol-model-formats/structure/mmcif/anisotropic'
+
+export const EllipsoidMeshParams = {
+    ...UnitsMeshParams,
+    sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
+    detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }),
+    ignoreHydrogens: PD.Boolean(false),
+}
+export type EllipsoidMeshParams = typeof EllipsoidMeshParams
+
+export function EllipsoidMeshVisual(materialId: number): UnitsVisual<EllipsoidMeshParams> {
+    return UnitsMeshVisual<EllipsoidMeshParams>({
+        defaultProps: PD.getDefaultValues(EllipsoidMeshParams),
+        createGeometry: createEllipsoidMesh,
+        createLocationIterator: ElementIterator.fromGroup,
+        getLoci: getElementLoci,
+        eachLocation: eachElement,
+        setUpdateState: (state: VisualUpdateState, newProps: PD.Values<EllipsoidMeshParams>, currentProps: PD.Values<EllipsoidMeshParams>) => {
+            state.createGeometry = (
+                newProps.sizeFactor !== currentProps.sizeFactor ||
+                newProps.detail !== currentProps.detail ||
+                newProps.ignoreHydrogens !== currentProps.ignoreHydrogens
+            )
+        }
+    }, materialId)
+}
+
+//
+
+export interface EllipsoidMeshProps {
+    detail: number,
+    sizeFactor: number,
+    ignoreHydrogens: boolean,
+}
+
+export function createEllipsoidMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: EllipsoidMeshProps, mesh?: Mesh): Mesh {
+    const { detail, sizeFactor } = props
+
+    const { elements, model } = unit;
+    const elementCount = elements.length;
+    const vertexCount = elementCount * sphereVertexCount(detail)
+    const builderState = MeshBuilder.createState(vertexCount, vertexCount / 2, mesh)
+
+    const atomSiteAnisotrop = AtomSiteAnisotrop.get(model)
+    if (!atomSiteAnisotrop) return Mesh.createEmpty(mesh)
+
+    const v = Vec3()
+    const m = Mat3()
+    const eigenvalues = Vec3()
+    const eigenvector1 = Vec3()
+    const eigenvector2 = Vec3()
+    const { elementToAnsiotrop, data } = atomSiteAnisotrop
+    const { U } = data
+    const space = data._schema.U.space
+    const pos = unit.conformation.invariantPosition
+    const l = StructureElement.Location.create()
+    l.unit = unit
+
+    for (let i = 0; i < elementCount; i++) {
+        const ei = elements[i]
+        const ai = elementToAnsiotrop[ei]
+        if (ai === -1) continue
+        if (props.ignoreHydrogens && isHydrogen(unit, ei)) continue
+
+        l.element = ei
+        pos(ei, v)
+
+        builderState.currentGroup = i
+        Tensor.toMat3(m, space, U.value(ai))
+        Mat3.symmtricFromLower(m, m)
+        Mat3.symmetricEigenvalues(eigenvalues, m)
+        Mat3.eigenvector(eigenvector1, m, eigenvalues[1])
+        Mat3.eigenvector(eigenvector2, m, eigenvalues[2])
+        for (let j = 0; j < 3; ++j) {
+            // show 50% probability surface, needs sqrt as U matrix is in angstrom-squared
+            // take abs of eigenvalue to avoid reflection
+            // multiply by given size-factor
+            eigenvalues[j] = sizeFactor * 1.5958 * Math.sqrt(Math.abs(eigenvalues[j]))
+        }
+
+        addEllipsoid(builderState, v, eigenvector2, eigenvector1, eigenvalues, detail)
+    }
+
+    return MeshBuilder.getMesh(builderState)
+}
\ No newline at end of file