diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts
index a846906bcbb19169b5598281158ab593d3d9d19b..9cb5fa513bebca3ea4d65335db9fb29f7752b4d0 100644
--- a/src/mol-canvas3d/canvas3d.ts
+++ b/src/mol-canvas3d/canvas3d.ts
@@ -428,6 +428,15 @@ namespace Canvas3D {
                         p.ambientOcclusion.radius = props.ambientOcclusion.radius
                         ValueCell.update(ssaoPass.values.uRadius, props.ambientOcclusion.radius)
                     }
+
+                    if (props.ambientOcclusion.edgeScale !== undefined) {
+                        p.ambientOcclusion.edgeScale = props.ambientOcclusion.edgeScale
+                        ValueCell.update(ssaoPass.values.uEdgeScale, props.ambientOcclusion.edgeScale * webgl.pixelRatio)
+                    }
+                    if (props.ambientOcclusion.edgeThreshold !== undefined) {
+                        p.ambientOcclusion.edgeThreshold = props.ambientOcclusion.edgeThreshold
+                        ValueCell.update(ssaoPass.values.uEdgeThreshold, props.ambientOcclusion.edgeThreshold)
+                    }
                 }
                 
                 if (props.renderer) renderer.setProps(props.renderer)
diff --git a/src/mol-canvas3d/passes/ssao-pass.ts b/src/mol-canvas3d/passes/ssao-pass.ts
index 91a6dd0fb67b8b52a25e3c3e9d03e37298d46e60..23dd4ee451df33d9587d0f6439c3ee7701cf39e2 100644
--- a/src/mol-canvas3d/passes/ssao-pass.ts
+++ b/src/mol-canvas3d/passes/ssao-pass.ts
@@ -25,6 +25,9 @@ const SSAOPassSchema = {
     uKernelSize: UniformSpec('i'),
     uBias: UniformSpec('f'),
     uRadius: UniformSpec('f'),
+
+    uEdgeScale: UniformSpec('f'),
+    uEdgeThreshold: UniformSpec('f'),
 }
 
 export const SSAOPassParams = {
@@ -32,6 +35,9 @@ export const SSAOPassParams = {
     kernelSize: PD.Numeric(4, { min: 1, max: 100, step: 1 }),
     bias: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
     radius: PD.Numeric(128, { min: 0, max: 256, step: 1 }),
+
+    edgeScale: PD.Numeric(1, { min: 0, max: 10, step: 1 }),
+    edgeThreshold: PD.Numeric(0.8, { min: 0, max: 1, step: 0.01 }),
 }
 export type SSAOPassProps = PD.Values<typeof SSAOPassParams>
 
@@ -47,6 +53,9 @@ export function getSSAOPassRenderable(ctx: WebGLContext, colorTexture: Texture,
         uKernelSize: ValueCell.create(p.kernelSize),
         uBias: ValueCell.create(p.bias),
         uRadius: ValueCell.create(p.radius),
+
+        uEdgeScale: ValueCell.create(p.edgeScale * ctx.pixelRatio),
+        uEdgeThreshold: ValueCell.create(p.edgeThreshold),
     }
 
     const schema = { ...SSAOPassSchema }
diff --git a/src/mol-gl/shader/passes/ssao.frag b/src/mol-gl/shader/passes/ssao.frag
index bbef5eba021b17e6bcf5f261c8d88f0765977e2e..0c46b26e3e4ee1003b536fe86bb55642cb64674d 100644
--- a/src/mol-gl/shader/passes/ssao.frag
+++ b/src/mol-gl/shader/passes/ssao.frag
@@ -11,6 +11,9 @@ uniform int uKernelSize;
 uniform float uBias;
 uniform float uRadius;
 
+uniform float uEdgeScale;
+uniform float uEdgeThreshold;
+
 const float noiseAmount = 0.0002;
 
 float noise(vec2 coords) {
@@ -31,13 +34,34 @@ float calcSSAO(in vec2 coords, in float depth) {
 			vec2 coordsDelta = coords + uRadius / float(uKernelSize) * vec2(float(i) / uTexSize.x, float(j) / uTexSize.y);
             coordsDelta += noiseAmount * (noise(coordsDelta) - 0.5) / uTexSize;
             coordsDelta = clamp(coordsDelta, 0.5 / uTexSize, 1.0 - 1.0 / uTexSize);
-			if (texture(tDepth, coordsDelta).r < depth) occlusionFactor += 1.0;
+			if (texture2D(tDepth, coordsDelta).r < depth) occlusionFactor += 1.0;
 		}
 	}
 
 	return occlusionFactor / float((2 * uKernelSize + 1) * (2 * uKernelSize + 1));
 }
 
+float calcEdgeDepth(in vec2 coords) {
+    vec2 invTexSize = 1.0 / uTexSize;
+    float halfScaleFloor = floor(uEdgeScale * 0.5);
+    float halfScaleCeil = ceil(uEdgeScale * 0.5);
+
+    vec2 bottomLeftUV = coords - invTexSize * halfScaleFloor;
+    vec2 topRightUV = coords + invTexSize * halfScaleCeil;  
+    vec2 bottomRightUV = coords + vec2(invTexSize.x * halfScaleCeil, -invTexSize.y * halfScaleFloor);
+    vec2 topLeftUV = coords + vec2(-invTexSize.x * halfScaleFloor, invTexSize.y * halfScaleCeil);
+
+    float depth0 = texture2D(tDepth, bottomLeftUV).r;
+    float depth1 = texture2D(tDepth, topRightUV).r;
+    float depth2 = texture2D(tDepth, bottomRightUV).r;
+    float depth3 = texture2D(tDepth, topLeftUV).r;
+
+    float depthFiniteDifference0 = depth1 - depth0;
+    float depthFiniteDifference1 = depth3 - depth2;
+
+    return sqrt(pow(depthFiniteDifference0, 2.0) + pow(depthFiniteDifference1, 2.0)) * 100.0;
+}
+
 void main(void) {
 	vec2 coords = gl_FragCoord.xy / uTexSize;
 	vec4 color = texture(tColor, coords);
@@ -48,6 +72,8 @@ void main(void) {
 		float occlusionFactor = calcSSAO(coords, depth);
 		color = mix(color, vec4(0.0, 0.0, 0.0, 1.0), uBias * occlusionFactor);
 	}
+
+    color.rgb *= (step(calcEdgeDepth(coords), uEdgeThreshold));
 	
 	gl_FragColor = color;
 }
\ No newline at end of file