diff --git a/CHANGELOG.md b/CHANGELOG.md
index a9bf194729db03f2118718ea4e2d54445edfc936..4857c42c00e625844c92a7ae8c9d84ed4a4a4fa8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ Note that since we don't clearly distinguish between a public and private interf
 - [Fix] Clone ``Canvas3DParams`` when creating a ``Canvas3D`` instance to prevent shared state between multiple instances
 - Add ``includeResidueTest`` option to ``alignAndSuperposeWithSIFTSMapping``
 - Add ``parentDisplay`` param for interactions representation.
+- Support for ``failIfMajorPerformanceCaveat`` webgl attribute. Add ``PluginConfig.General.AllowMajorPerformanceCaveat`` and ``allow-major-performance-caveat`` Viewer GET param.
 
 ## [v3.16.0] - 2022-08-25
 
diff --git a/src/apps/viewer/app.ts b/src/apps/viewer/app.ts
index 4c8df4e1bc8a25a6fc67e46fce07d75967e79234..c36b5fccb402f58ef645bba5cfc8c0b5042c666a 100644
--- a/src/apps/viewer/app.ts
+++ b/src/apps/viewer/app.ts
@@ -89,6 +89,7 @@ const DefaultViewerOptions = {
     pickPadding: PluginConfig.General.PickPadding.defaultValue,
     enableWboit: PluginConfig.General.EnableWboit.defaultValue,
     preferWebgl1: PluginConfig.General.PreferWebGl1.defaultValue,
+    allowMajorPerformanceCaveat: PluginConfig.General.AllowMajorPerformanceCaveat.defaultValue,
 
     viewportShowExpand: PluginConfig.Viewport.ShowExpand.defaultValue,
     viewportShowControls: PluginConfig.Viewport.ShowControls.defaultValue,
@@ -159,6 +160,7 @@ export class Viewer {
                 [PluginConfig.General.PickPadding, o.pickPadding],
                 [PluginConfig.General.EnableWboit, o.enableWboit],
                 [PluginConfig.General.PreferWebGl1, o.preferWebgl1],
+                [PluginConfig.General.AllowMajorPerformanceCaveat, o.allowMajorPerformanceCaveat],
                 [PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
                 [PluginConfig.Viewport.ShowControls, o.viewportShowControls],
                 [PluginConfig.Viewport.ShowSettings, o.viewportShowSettings],
diff --git a/src/apps/viewer/index.html b/src/apps/viewer/index.html
index c63978410e9ee354bfa5ac2d916dd2104d941b1d..aba056ce79dcf5327f99447a04a4cb3f5c9ac237 100644
--- a/src/apps/viewer/index.html
+++ b/src/apps/viewer/index.html
@@ -61,6 +61,7 @@
             var pickPadding = getParam('pick-padding', '[^&]+').trim();
             var disableWboit = getParam('disable-wboit', '[^&]+').trim() === '1';
             var preferWebgl1 = getParam('prefer-webgl1', '[^&]+').trim() === '1' || void 0;
+            var allowMajorPerformanceCaveat = getParam('allow-major-performance-caveat', '[^&]+').trim() === '1';
 
             molstar.Viewer.create('app', {
                 layoutShowControls: !hideControls,
@@ -76,6 +77,7 @@
                 pickPadding: isNaN(parseFloat(pickPadding)) ? 1 : parseFloat(pickPadding),
                 enableWboit: disableWboit ? false : void 0, // use default value if disable-wboit is not set
                 preferWebgl1: preferWebgl1,
+                allowMajorPerformanceCaveat: allowMajorPerformanceCaveat,
             }).then(viewer => {
                 var snapshotId = getParam('snapshot-id', '[^&]+').trim();
                 if (snapshotId) viewer.setRemoteSnapshot(snapshotId);
diff --git a/src/extensions/geo-export/ui.tsx b/src/extensions/geo-export/ui.tsx
index 837c59021f5fb38fa342a315846ccd3eb8277466..78aa038e85dd6dcd5c6433940c2ea3f6a4e300c0 100644
--- a/src/extensions/geo-export/ui.tsx
+++ b/src/extensions/geo-export/ui.tsx
@@ -60,6 +60,8 @@ export class GeometryExporterUI extends CollapsableControls<{}, State> {
     }
 
     componentDidMount() {
+        if (!this.plugin.canvas3d) return;
+
         const merged = merge(
             this.controls.behaviors.params,
             this.plugin.canvas3d!.reprCount
diff --git a/src/extensions/mp4-export/controls.ts b/src/extensions/mp4-export/controls.ts
index a8edd32816a919b64d0ce1337dc6e6084d2ed5bd..b6dadcc5e80f79a5f6a78201e6cde93c389e2012 100644
--- a/src/extensions/mp4-export/controls.ts
+++ b/src/extensions/mp4-export/controls.ts
@@ -118,11 +118,13 @@ export class Mp4Controls extends PluginComponent {
     }
 
     private init() {
+        if (!this.plugin.canvas3d) return;
+
         this.subscribe(this.plugin.managers.animation.events.updated.pipe(debounceTime(16)), () => {
             this.sync();
         });
 
-        this.subscribe(this.plugin.canvas3d?.resized!, () => this.syncInfo());
+        this.subscribe(this.plugin.canvas3d.resized, () => this.syncInfo());
         this.subscribe(this.plugin.helpers.viewportScreenshot?.events.previewed!, () => this.syncInfo());
 
         this.subscribe(this.plugin.behaviors.state.isBusy, b => this.updateCanApply(b));
diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts
index e0c3335651cbb1cd19be7ea1f1417d3fa77a9d0e..e55c95a26bdd19be0b618dac28130cf37d0e5de0 100644
--- a/src/mol-canvas3d/canvas3d.ts
+++ b/src/mol-canvas3d/canvas3d.ts
@@ -117,6 +117,7 @@ interface Canvas3DContext {
 
 namespace Canvas3DContext {
     export const DefaultAttribs = {
+        failIfMajorPerformanceCaveat: false,
         /** true by default to avoid issues with Safari (Jan 2021) */
         antialias: true,
         /** true to support multiple Canvas3D objects with a single context */
@@ -132,8 +133,9 @@ namespace Canvas3DContext {
 
     export function fromCanvas(canvas: HTMLCanvasElement, assetManager: AssetManager, attribs: Partial<Attribs> = {}): Canvas3DContext {
         const a = { ...DefaultAttribs, ...attribs };
-        const { antialias, preserveDrawingBuffer, pixelScale, preferWebGl1 } = a;
+        const { failIfMajorPerformanceCaveat, antialias, preserveDrawingBuffer, pixelScale, preferWebGl1 } = a;
         const gl = getGLContext(canvas, {
+            failIfMajorPerformanceCaveat,
             antialias,
             preserveDrawingBuffer,
             alpha: true, // the renderer requires an alpha channel
diff --git a/src/mol-plugin-ui/controls/screenshot.tsx b/src/mol-plugin-ui/controls/screenshot.tsx
index a90e0778da21ac8b193541c2f222d38275ebcdee..90be286e6a7955ff67bd4024532440124fb6e0c7 100644
--- a/src/mol-plugin-ui/controls/screenshot.tsx
+++ b/src/mol-plugin-ui/controls/screenshot.tsx
@@ -24,8 +24,9 @@ export interface ScreenshotPreviewProps {
 
 const _ScreenshotPreview = (props: ScreenshotPreviewProps) => {
     const { plugin, cropFrameColor } = props;
+    const helper = plugin.helpers.viewportScreenshot;
+    if (!helper) return null;
 
-    const helper = plugin.helpers.viewportScreenshot!;
     const [currentCanvas, setCurrentCanvas] = useState<HTMLCanvasElement | null>(null);
     const canvasRef = useRef<HTMLCanvasElement | null>(null);
     const propsRef = useRef(props);
@@ -52,7 +53,7 @@ const _ScreenshotPreview = (props: ScreenshotPreviewProps) => {
         function preview() {
             const p = propsRef.current;
             if (!p.suspend && canvasRef.current) {
-                drawPreview(helper, canvasRef.current, p.customBackground, p.borderColor, p.borderWidth);
+                drawPreview(helper!, canvasRef.current, p.customBackground, p.borderColor, p.borderWidth);
             }
 
             if (!canvasRef.current) isDirty = true;
@@ -151,9 +152,11 @@ function drawPreview(helper: ViewportScreenshotHelper, target: HTMLCanvasElement
 
 function ViewportFrame({ plugin, canvas, color = 'rgba(255, 87, 45, 0.75)' }: { plugin: PluginContext, canvas: HTMLCanvasElement | null, color?: string }) {
     const helper = plugin.helpers.viewportScreenshot;
-    const params = useBehavior(helper?.behaviors.values!);
-    const cropParams = useBehavior(helper?.behaviors.cropParams!);
-    const crop = useBehavior(helper?.behaviors.relativeCrop!);
+    if (!helper || !canvas) return null;
+
+    const params = useBehavior(helper.behaviors.values);
+    const cropParams = useBehavior(helper.behaviors.cropParams);
+    const crop = useBehavior(helper.behaviors.relativeCrop!);
     const cropFrameRef = useRef<Viewport>({ x: 0, y: 0, width: 0, height: 0 });
     useBehavior(params?.resolution.name === 'viewport' ? plugin.canvas3d?.resized : void 0);
 
@@ -161,8 +164,6 @@ function ViewportFrame({ plugin, canvas, color = 'rgba(255, 87, 45, 0.75)' }: {
     const [start, setStart] = useState([0, 0]);
     const [current, setCurrent] = useState([0, 0]);
 
-    if (!helper || !canvas) return null;
-
     const { width, height } = helper.getSizeAndViewport();
 
     const frame = getViewportFrame(width, height, canvas.clientWidth, canvas.clientHeight);
diff --git a/src/mol-plugin-ui/viewport/canvas.tsx b/src/mol-plugin-ui/viewport/canvas.tsx
index 616c145d1bb5033e6ae956f125fe780863ff6f21..ab49d381a4e449ff238eea734d1b0f5def960e86 100644
--- a/src/mol-plugin-ui/viewport/canvas.tsx
+++ b/src/mol-plugin-ui/viewport/canvas.tsx
@@ -59,7 +59,7 @@ export class ViewportCanvas extends PluginUIComponent<ViewportCanvasParams, View
         return <div className='msp-no-webgl'>
             <div>
                 <p><b>WebGL does not seem to be available.</b></p>
-                <p>This can be caused by an outdated browser, graphics card driver issue, or bad weather. Sometimes, just restarting the browser helps.</p>
+                <p>This can be caused by an outdated browser, graphics card driver issue, or bad weather. Sometimes, just restarting the browser helps. Also, make sure hardware acceleration is enabled in your browser.</p>
                 <p>For a list of supported browsers, refer to <a href='http://caniuse.com/#feat=webgl' target='_blank'>http://caniuse.com/#feat=webgl</a>.</p>
             </div>
         </div>;
diff --git a/src/mol-plugin-ui/viewport/screenshot.tsx b/src/mol-plugin-ui/viewport/screenshot.tsx
index 135beee19ae88e1204d2cda6eaa926e91b65eb8d..bf4335cbfcca097f390af9d6c51eefd71508208b 100644
--- a/src/mol-plugin-ui/viewport/screenshot.tsx
+++ b/src/mol-plugin-ui/viewport/screenshot.tsx
@@ -96,15 +96,18 @@ export class DownloadScreenshotControls extends PluginUIComponent<{ close: () =>
 }
 
 function ScreenshotParams({ plugin, isDisabled }: { plugin: PluginContext, isDisabled: boolean }) {
-    const helper = plugin.helpers.viewportScreenshot!;
-    const values = useBehavior(helper.behaviors.values);
+    const helper = plugin.helpers.viewportScreenshot;
+    if (!helper) return null;
 
+    const values = useBehavior(helper.behaviors.values);
     return <ParameterControls params={helper.params} values={values} onChangeValues={v => helper.behaviors.values.next(v)} isDisabled={isDisabled} />;
 }
 
 function CropControls({ plugin }: { plugin: PluginContext }) {
     const helper = plugin.helpers.viewportScreenshot;
-    const cropParams = useBehavior(helper?.behaviors.cropParams!);
+    if (!helper) return null;
+
+    const cropParams = useBehavior(helper.behaviors.cropParams);
     useBehavior(helper?.behaviors.relativeCrop);
 
     if (!helper) return null;
diff --git a/src/mol-plugin-ui/viewport/simple-settings.tsx b/src/mol-plugin-ui/viewport/simple-settings.tsx
index 122e11f8a64fa9d987db949223698ec55fc66771..77772e62b3047fbb12b8c3a77897456380b734ff 100644
--- a/src/mol-plugin-ui/viewport/simple-settings.tsx
+++ b/src/mol-plugin-ui/viewport/simple-settings.tsx
@@ -22,6 +22,8 @@ import { ViewportHelpContent } from './help';
 
 export class SimpleSettingsControl extends PluginUIComponent {
     componentDidMount() {
+        if (!this.plugin.canvas3d) return;
+
         this.subscribe(this.plugin.events.canvas3d.settingsUpdated, () => this.forceUpdate());
 
         this.subscribe(this.plugin.canvas3d!.camera.stateChanged, state => {
diff --git a/src/mol-plugin/config.ts b/src/mol-plugin/config.ts
index e087c3468e04b818d23357059b899aa9eb031db9..707dd37d0208d56c3080c6695a9046012fb663ad 100644
--- a/src/mol-plugin/config.ts
+++ b/src/mol-plugin/config.ts
@@ -35,6 +35,7 @@ export const PluginConfig = {
         // as of Oct 1 2021, WebGL 2 doesn't work on iOS 15.
         // TODO: check back in a few weeks to see if it was fixed
         PreferWebGl1: item('plugin-config.prefer-webgl1', PluginFeatureDetection.preferWebGl1),
+        AllowMajorPerformanceCaveat: item('plugin-config.allow-major-performance-caveat', false),
     },
     State: {
         DefaultServer: item('plugin-state.server', 'https://webchem.ncbr.muni.cz/molstar-state'),
diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts
index 405087080f0c118dece7b9c2ec541134ddcf23d0..4db484ae17be78415b1bba5e237e019c2b93b019 100644
--- a/src/mol-plugin/context.ts
+++ b/src/mol-plugin/context.ts
@@ -201,7 +201,8 @@ export class PluginContext {
                 const pickPadding = this.config.get(PluginConfig.General.PickPadding) ?? 1;
                 const enableWboit = this.config.get(PluginConfig.General.EnableWboit) || false;
                 const preferWebGl1 = this.config.get(PluginConfig.General.PreferWebGl1) || false;
-                (this.canvas3dContext as Canvas3DContext) = Canvas3DContext.fromCanvas(canvas, this.managers.asset, { antialias, preserveDrawingBuffer, pixelScale, pickScale, pickPadding, enableWboit, preferWebGl1 });
+                const failIfMajorPerformanceCaveat = !(this.config.get(PluginConfig.General.AllowMajorPerformanceCaveat) ?? false);
+                (this.canvas3dContext as Canvas3DContext) = Canvas3DContext.fromCanvas(canvas, this.managers.asset, { antialias, preserveDrawingBuffer, pixelScale, pickScale, pickPadding, enableWboit, preferWebGl1, failIfMajorPerformanceCaveat });
             }
             (this.canvas3d as Canvas3D) = Canvas3D.create(this.canvas3dContext!);
             this.canvas3dInit.next(true);