diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json
index 2826fb5394..b04596c0dd 100644
--- a/invokeai/frontend/web/public/locales/en.json
+++ b/invokeai/frontend/web/public/locales/en.json
@@ -1646,7 +1646,11 @@
         "storeNotInitialized": "Store is not initialized"
     },
     "controlLayers": {
+        "resetCanvas": "Reset Canvas",
         "resetAll": "Reset All",
+        "clearCaches": "Clear Caches",
+        "recalculateRects": "Recalculate Rects",
+        "clipToBbox": "Clip Strokes to Bbox",
         "addLayer": "Add Layer",
         "moveToFront": "Move to Front",
         "moveToBack": "Move to Back",
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx
deleted file mode 100644
index 58942034bc..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import {
-  Button,
-  Checkbox,
-  Flex,
-  FormControl,
-  FormLabel,
-  IconButton,
-  Popover,
-  PopoverBody,
-  PopoverContent,
-  PopoverTrigger,
-} from '@invoke-ai/ui-library';
-import { useStore } from '@nanostores/react';
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { CanvasSettingsDynamicGridToggle } from 'features/controlLayers/components/CanvasSettingsDynamicGridToggle';
-import { $canvasManager } from 'features/controlLayers/konva/CanvasManager';
-import { clipToBboxChanged, invertScrollChanged } from 'features/controlLayers/store/canvasV2Slice';
-import type { ChangeEvent } from 'react';
-import { memo, useCallback } from 'react';
-import { useTranslation } from 'react-i18next';
-import { RiSettings4Fill } from 'react-icons/ri';
-
-const ControlLayersSettingsPopover = () => {
-  const { t } = useTranslation();
-  const dispatch = useAppDispatch();
-  const canvasManager = useStore($canvasManager);
-  const clipToBbox = useAppSelector((s) => s.canvasV2.settings.clipToBbox);
-  const invertScroll = useAppSelector((s) => s.canvasV2.tool.invertScroll);
-  const onChangeInvertScroll = useCallback(
-    (e: ChangeEvent<HTMLInputElement>) => dispatch(invertScrollChanged(e.target.checked)),
-    [dispatch]
-  );
-  const onChangeClipToBbox = useCallback(
-    (e: ChangeEvent<HTMLInputElement>) => dispatch(clipToBboxChanged(e.target.checked)),
-    [dispatch]
-  );
-  const clearCaches = useCallback(() => {
-    canvasManager?.cache.clearAll();
-  }, [canvasManager]);
-  const calculateBboxes = useCallback(() => {
-    if (!canvasManager) {
-      return;
-    }
-    const adapters = [
-      ...canvasManager.rasterLayerAdapters.values(),
-      ...canvasManager.controlLayerAdapters.values(),
-      ...canvasManager.regionalGuidanceAdapters.values(),
-      ...canvasManager.inpaintMaskAdapters.values(),
-    ];
-    for (const adapter of adapters) {
-      adapter.transformer.requestRectCalculation();
-    }
-  }, [canvasManager]);
-  return (
-    <Popover isLazy>
-      <PopoverTrigger>
-        <IconButton aria-label={t('common.settingsLabel')} icon={<RiSettings4Fill />} />
-      </PopoverTrigger>
-      <PopoverContent>
-        <PopoverBody>
-          <Flex direction="column" gap={2}>
-            <FormControl w="full">
-              <FormLabel flexGrow={1}>{t('unifiedCanvas.invertBrushSizeScrollDirection')}</FormLabel>
-              <Checkbox isChecked={invertScroll} onChange={onChangeInvertScroll} />
-            </FormControl>
-            <FormControl w="full">
-              <FormLabel flexGrow={1}>{t('unifiedCanvas.clipToBbox')}</FormLabel>
-              <Checkbox isChecked={clipToBbox} onChange={onChangeClipToBbox} />
-            </FormControl>
-            <CanvasSettingsDynamicGridToggle />
-            <Button onClick={clearCaches} size="sm">
-              Clear Caches
-            </Button>
-            <Button onClick={calculateBboxes} size="sm">
-              Calculate Bboxes
-            </Button>
-          </Flex>
-        </PopoverBody>
-      </PopoverContent>
-    </Popover>
-  );
-};
-
-export default memo(ControlLayersSettingsPopover);
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersToolbar.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersToolbar.tsx
index 69ec88c5cf..ec968f19f8 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersToolbar.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersToolbar.tsx
@@ -1,63 +1,38 @@
 /* eslint-disable i18next/no-literal-string */
-import { Flex, Switch } from '@invoke-ai/ui-library';
-import { useStore } from '@nanostores/react';
+import { Flex, Spacer } from '@invoke-ai/ui-library';
 import { useAppSelector } from 'app/store/storeHooks';
 import { CanvasResetViewButton } from 'features/controlLayers/components/CanvasResetViewButton';
 import { CanvasScale } from 'features/controlLayers/components/CanvasScale';
-import ControlLayersSettingsPopover from 'features/controlLayers/components/ControlLayersSettingsPopover';
-import { ResetCanvasButton } from 'features/controlLayers/components/ResetCanvasButton';
+import { CanvasSettingsPopover } from 'features/controlLayers/components/Settings/CanvasSettingsPopover';
 import { ToolBrushWidth } from 'features/controlLayers/components/Tool/ToolBrushWidth';
 import { ToolChooser } from 'features/controlLayers/components/Tool/ToolChooser';
 import { ToolEraserWidth } from 'features/controlLayers/components/Tool/ToolEraserWidth';
 import { ToolFillColorPicker } from 'features/controlLayers/components/Tool/ToolFillColorPicker';
 import { UndoRedoButtonGroup } from 'features/controlLayers/components/UndoRedoButtonGroup';
-import { $canvasManager } from 'features/controlLayers/konva/CanvasManager';
+import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
 import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton';
 import { ViewerToggleMenu } from 'features/gallery/components/ImageViewer/ViewerToggleMenu';
-import type { ChangeEvent } from 'react';
-import { memo, useCallback } from 'react';
+import { memo } from 'react';
 
 export const ControlLayersToolbar = memo(() => {
   const tool = useAppSelector((s) => s.canvasV2.tool.selected);
-  const canvasManager = useStore($canvasManager);
-  const onChangeDebugging = useCallback(
-    (e: ChangeEvent<HTMLInputElement>) => {
-      if (!canvasManager) {
-        return;
-      }
-      if (e.target.checked) {
-        canvasManager.enableDebugging();
-      } else {
-        canvasManager.disableDebugging();
-      }
-    },
-    [canvasManager]
-  );
   return (
-    <Flex w="full" gap={2}>
-      <Flex flex={1} justifyContent="center">
-        <Flex gap={2} marginInlineEnd="auto" alignItems="center">
-          <ToggleProgressButton />
-          <ToolChooser />
-        </Flex>
-      </Flex>
-      <Flex flex={1} gap={2} justifyContent="center" alignItems="center">
+    <CanvasManagerProviderGate>
+      <Flex w="full" gap={2} alignItems="center">
+        <ToggleProgressButton />
+        <ToolChooser />
         {tool === 'brush' && <ToolBrushWidth />}
         {tool === 'eraser' && <ToolEraserWidth />}
+        <Spacer />
+        <CanvasScale />
+        <CanvasResetViewButton />
+        <Spacer />
+        <ToolFillColorPicker />
+        <UndoRedoButtonGroup />
+        <CanvasSettingsPopover />
+        <ViewerToggleMenu />
       </Flex>
-      <CanvasScale />
-      <CanvasResetViewButton />
-      <Switch onChange={onChangeDebugging}>debug</Switch>
-      <Flex flex={1} justifyContent="center">
-        <Flex gap={2} marginInlineStart="auto" alignItems="center">
-          <ToolFillColorPicker />
-          <UndoRedoButtonGroup />
-          <ControlLayersSettingsPopover />
-          <ResetCanvasButton />
-          <ViewerToggleMenu />
-        </Flex>
-      </Flex>
-    </Flex>
+    </CanvasManagerProviderGate>
   );
 });
 
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ResetCanvasButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ResetCanvasButton.tsx
deleted file mode 100644
index f0880b6e02..0000000000
--- a/invokeai/frontend/web/src/features/controlLayers/components/ResetCanvasButton.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { IconButton } from '@invoke-ai/ui-library';
-import { useAppDispatch } from 'app/store/storeHooks';
-import { canvasReset } from 'features/controlLayers/store/canvasV2Slice';
-import { memo, useCallback } from 'react';
-import { PiTrashBold } from 'react-icons/pi';
-
-export const ResetCanvasButton = memo(() => {
-  const dispatch = useAppDispatch();
-  const onClick = useCallback(() => {
-    dispatch(canvasReset());
-  }, [dispatch]);
-  return <IconButton onClick={onClick} icon={<PiTrashBold />} aria-label="Reset canvas" colorScheme="error" />;
-});
-
-ResetCanvasButton.displayName = 'ResetCanvasButton';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClearCachesButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClearCachesButton.tsx
new file mode 100644
index 0000000000..a3fd6c7317
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClearCachesButton.tsx
@@ -0,0 +1,19 @@
+import { Button } from '@invoke-ai/ui-library';
+import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+
+export const CanvasSettingsClearCachesButton = memo(() => {
+  const { t } = useTranslation();
+  const canvasManager = useCanvasManager();
+  const clearCaches = useCallback(() => {
+    canvasManager.cache.clearAll();
+  }, [canvasManager]);
+  return (
+    <Button onClick={clearCaches} size="sm" colorScheme="warning">
+      {t('controlLayers.clearCaches')}
+    </Button>
+  );
+});
+
+CanvasSettingsClearCachesButton.displayName = 'CanvasSettingsClearCachesButton';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClipToBboxCheckbox.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClipToBboxCheckbox.tsx
new file mode 100644
index 0000000000..205c36070c
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsClipToBboxCheckbox.tsx
@@ -0,0 +1,24 @@
+import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { clipToBboxChanged } from 'features/controlLayers/store/canvasV2Slice';
+import type { ChangeEvent } from 'react';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+
+export const CanvasSettingsClipToBboxCheckbox = memo(() => {
+  const { t } = useTranslation();
+  const dispatch = useAppDispatch();
+  const clipToBbox = useAppSelector((s) => s.canvasV2.settings.clipToBbox);
+  const onChange = useCallback(
+    (e: ChangeEvent<HTMLInputElement>) => dispatch(clipToBboxChanged(e.target.checked)),
+    [dispatch]
+  );
+  return (
+    <FormControl w="full">
+      <FormLabel flexGrow={1}>{t('controlLayers.clipToBbox')}</FormLabel>
+      <Checkbox isChecked={clipToBbox} onChange={onChange} />
+    </FormControl>
+  );
+});
+
+CanvasSettingsClipToBboxCheckbox.displayName = 'CanvasSettingsClipToBboxCheckbox';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasSettingsDynamicGridToggle.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsDynamicGridSwitch.tsx
similarity index 85%
rename from invokeai/frontend/web/src/features/controlLayers/components/CanvasSettingsDynamicGridToggle.tsx
rename to invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsDynamicGridSwitch.tsx
index 916abc5574..71ffe1e729 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasSettingsDynamicGridToggle.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsDynamicGridSwitch.tsx
@@ -4,7 +4,7 @@ import { settingsDynamicGridToggled } from 'features/controlLayers/store/canvasV
 import { memo, useCallback } from 'react';
 import { useTranslation } from 'react-i18next';
 
-export const CanvasSettingsDynamicGridToggle = memo(() => {
+export const CanvasSettingsDynamicGridSwitch = memo(() => {
   const { t } = useTranslation();
   const dispatch = useAppDispatch();
   const dynamicGrid = useAppSelector((s) => s.canvasV2.settings.dynamicGrid);
@@ -22,4 +22,4 @@ export const CanvasSettingsDynamicGridToggle = memo(() => {
   );
 });
 
-CanvasSettingsDynamicGridToggle.displayName = 'CanvasSettingsDynamicGridToggle';
+CanvasSettingsDynamicGridSwitch.displayName = 'CanvasSettingsDynamicGridSwitch';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsInvertScrollCheckbox.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsInvertScrollCheckbox.tsx
new file mode 100644
index 0000000000..1c9a4f174c
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsInvertScrollCheckbox.tsx
@@ -0,0 +1,24 @@
+import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { invertScrollChanged } from 'features/controlLayers/store/canvasV2Slice';
+import type { ChangeEvent } from 'react';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+
+export const CanvasSettingsInvertScrollCheckbox = memo(() => {
+  const { t } = useTranslation();
+  const dispatch = useAppDispatch();
+  const invertScroll = useAppSelector((s) => s.canvasV2.tool.invertScroll);
+  const onChange = useCallback(
+    (e: ChangeEvent<HTMLInputElement>) => dispatch(invertScrollChanged(e.target.checked)),
+    [dispatch]
+  );
+  return (
+    <FormControl w="full">
+      <FormLabel flexGrow={1}>{t('unifiedCanvas.invertBrushSizeScrollDirection')}</FormLabel>
+      <Checkbox isChecked={invertScroll} onChange={onChange} />
+    </FormControl>
+  );
+});
+
+CanvasSettingsInvertScrollCheckbox.displayName = 'CanvasSettingsInvertScrollCheckbox';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsPopover.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsPopover.tsx
new file mode 100644
index 0000000000..c0c91ba936
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsPopover.tsx
@@ -0,0 +1,61 @@
+import {
+  Divider,
+  Flex,
+  IconButton,
+  Popover,
+  PopoverArrow,
+  PopoverBody,
+  PopoverContent,
+  PopoverTrigger,
+  useShiftModifier,
+} from '@invoke-ai/ui-library';
+import { CanvasSettingsClearCachesButton } from 'features/controlLayers/components/Settings/CanvasSettingsClearCachesButton';
+import { CanvasSettingsClipToBboxCheckbox } from 'features/controlLayers/components/Settings/CanvasSettingsClipToBboxCheckbox';
+import { CanvasSettingsDynamicGridSwitch } from 'features/controlLayers/components/Settings/CanvasSettingsDynamicGridSwitch';
+import { CanvasSettingsInvertScrollCheckbox } from 'features/controlLayers/components/Settings/CanvasSettingsInvertScrollCheckbox';
+import { CanvasSettingsRecalculateRectsButton } from 'features/controlLayers/components/Settings/CanvasSettingsRecalculateRectsButton';
+import { CanvasSettingsResetButton } from 'features/controlLayers/components/Settings/CanvasSettingsResetButton';
+import { memo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { RiSettings4Fill } from 'react-icons/ri';
+
+export const CanvasSettingsPopover = memo(() => {
+  const { t } = useTranslation();
+  return (
+    <Popover isLazy>
+      <PopoverTrigger>
+        <IconButton aria-label={t('common.settingsLabel')} icon={<RiSettings4Fill />} />
+      </PopoverTrigger>
+      <PopoverContent>
+        <PopoverArrow />
+        <PopoverBody>
+          <Flex direction="column" gap={2}>
+            <CanvasSettingsInvertScrollCheckbox />
+            <CanvasSettingsClipToBboxCheckbox />
+            <CanvasSettingsDynamicGridSwitch />
+            <CanvasSettingsResetButton />
+            <DebugSettings />
+          </Flex>
+        </PopoverBody>
+      </PopoverContent>
+    </Popover>
+  );
+});
+
+CanvasSettingsPopover.displayName = 'CanvasSettingsPopover';
+
+const DebugSettings = () => {
+  const shift = useShiftModifier();
+
+  if (!shift) {
+    return null;
+  }
+
+  return (
+    <>
+      <Divider />
+      <CanvasSettingsClearCachesButton />
+      <CanvasSettingsRecalculateRectsButton />
+    </>
+  );
+};
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsRecalculateRectsButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsRecalculateRectsButton.tsx
new file mode 100644
index 0000000000..7b3be93bdd
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsRecalculateRectsButton.tsx
@@ -0,0 +1,28 @@
+import { Button } from '@invoke-ai/ui-library';
+import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+
+export const CanvasSettingsRecalculateRectsButton = memo(() => {
+  const { t } = useTranslation();
+  const canvasManager = useCanvasManager();
+  const onClick = useCallback(() => {
+    const adapters = [
+      ...canvasManager.rasterLayerAdapters.values(),
+      ...canvasManager.controlLayerAdapters.values(),
+      ...canvasManager.regionalGuidanceAdapters.values(),
+      ...canvasManager.inpaintMaskAdapters.values(),
+    ];
+    for (const adapter of adapters) {
+      adapter.transformer.requestRectCalculation();
+    }
+  }, [canvasManager]);
+
+  return (
+    <Button onClick={onClick} size="sm" colorScheme="warning">
+      {t('controlLayers.recalculateRects')}
+    </Button>
+  );
+});
+
+CanvasSettingsRecalculateRectsButton.displayName = 'CanvasSettingsRecalculateRectsButton';
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsResetButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsResetButton.tsx
new file mode 100644
index 0000000000..a40a3f3aae
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlLayers/components/Settings/CanvasSettingsResetButton.tsx
@@ -0,0 +1,20 @@
+import { Button } from '@invoke-ai/ui-library';
+import { useAppDispatch } from 'app/store/storeHooks';
+import { canvasReset } from 'features/controlLayers/store/canvasV2Slice';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+
+export const CanvasSettingsResetButton = memo(() => {
+  const { t } = useTranslation();
+  const dispatch = useAppDispatch();
+  const onClick = useCallback(() => {
+    dispatch(canvasReset());
+  }, [dispatch]);
+  return (
+    <Button onClick={onClick} colorScheme="error" size="sm">
+      {t('controlLayers.resetCanvas')}
+    </Button>
+  );
+});
+
+CanvasSettingsResetButton.displayName = 'CanvasSettingsResetButton';