diff --git a/common/util.js b/common/util.js
index 4029c6744e2ca274e4a4be75e9dab633a17c904f..a9fd9d6c0c939b5bb05ef281543f74d56b105a29 100644
--- a/common/util.js
+++ b/common/util.js
@@ -204,4 +204,26 @@
     const { labelIndex, ngId } = deserialiseParcRegionId(labelIndexId)
   }
 
+  const getPad = ({ length, pad }) => {
+    if (pad.length !== 1) throw new Error(`pad needs to be precisely 1 character`)
+    return input => {
+      const padNum = Math.max(input.toString().length - length, 0)
+      const padString = Array(padNum).fill(pad).join('')
+      return `${padString}${input}`
+    }
+  }
+
+  exports.getDateString = () => {
+    const d = new Date()
+    const pad2 = getPad({ pad: '0', length: 2 })
+
+    const year = d.getFullYear()
+    const month = d.getMonth() + 1
+    const date = d.getDate()
+
+    const hr = d.getHours()
+    const min = d.getMinutes()
+    return `${year}${pad2(month)}${pad2(date)}_${pad2(hr)}${pad2(min)}`
+  }
+
 })(typeof exports === 'undefined' ? module.exports : exports)
diff --git a/docs/releases/v2.4.0.md b/docs/releases/v2.4.0.md
index 9e047ec46c57a81a3e1bf0a306bd4cedf95a8914..bd1bb94a609d2a92ad4fc44b5d1abd8da1c0f7c7 100644
--- a/docs/releases/v2.4.0.md
+++ b/docs/releases/v2.4.0.md
@@ -7,3 +7,5 @@
 ## Under the hood stuff
 
 - refactored code, added additional test coverage
+- reworked screenshot component, removed html2canvas dependency
+- deprecated `getToastHandler` and `geModalHandler` in plugin API
\ No newline at end of file
diff --git a/package.json b/package.json
index e62868a629bca945df2f56c4db4dbb9b2bb47a67..461fde5d6a38a9176f6a7c387ae2e1f8d2b317bb 100644
--- a/package.json
+++ b/package.json
@@ -51,7 +51,6 @@
     "glob": "^7.1.6",
     "hammerjs": "^2.0.8",
     "html-webpack-plugin": "^3.2.0",
-    "html2canvas": "^1.0.0-rc.1",
     "jasmine": "^3.1.0",
     "jasmine-core": "^3.5.0",
     "jasmine-marbles": "^0.6.0",
diff --git a/src/atlasViewer/atlasViewer.apiService.service.ts b/src/atlasViewer/atlasViewer.apiService.service.ts
index 75c5e5a231f25784773cb95a2941ed4692b2c834..05eba0de7e6c56770513349a68046df851987d77 100644
--- a/src/atlasViewer/atlasViewer.apiService.service.ts
+++ b/src/atlasViewer/atlasViewer.apiService.service.ts
@@ -15,8 +15,6 @@ import {
 } from "src/services/stateStore.service";
 import { FRAGMENT_EMIT_RED } from "src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component";
 import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
-import { ModalHandler } from "../util/pluginHandlerClasses/modalHandler";
-import { ToastHandler } from "../util/pluginHandlerClasses/toastHandler";
 import { IPluginManifest, PluginServices } from "./pluginUnit";
 
 declare let window
@@ -35,7 +33,6 @@ interface IGetUserSelectRegionPr{
 }
 
 export const CANCELLABLE_DIALOG = 'CANCELLABLE_DIALOG'
-export const GET_TOAST_HANDLER_TOKEN = 'GET_TOAST_HANDLER_TOKEN'
 
 export interface ILoadMesh {
   type: 'VTK'
@@ -161,7 +158,6 @@ export class AtlasViewerAPIServices implements OnDestroy{
     private zone: NgZone,
     private pluginService: PluginServices,
     @Optional() @Inject(CANCELLABLE_DIALOG) openCancellableDialog: (message: string, options: any) => () => void,
-    @Optional() @Inject(GET_TOAST_HANDLER_TOKEN) private getToastHandler: Function,
     @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor
   ) {
     if (clickInterceptor) {
@@ -255,39 +251,12 @@ export class AtlasViewerAPIServices implements OnDestroy{
       },
       uiHandle : {
         getModalHandler : () => {
-          const handler = new ModalHandler()
-          let modalRef
-          handler.show = () => {
-            /**
-             * TODO enable
-             * temporarily disabled
-             */
-            // modalRef = this.modalService.show(ModalUnit, {
-            //   initialState : {
-            //     title : handler.title,
-            //     body : handler.body
-            //       ? handler.body
-            //       : 'handler.body has yet been defined ...',
-            //     footer : handler.footer
-            //   },
-            //   class : this.darktheme ? 'darktheme' : 'not-darktheme',
-            //   backdrop : handler.dismissable ? true : 'static',
-            //   keyboard : handler.dismissable
-            // })
-          }
-          handler.hide = () => {
-            if (modalRef) {
-              modalRef.hide()
-              modalRef = null
-            }
-          }
-          return handler
+          throw new Error(`uihandle.getModalHandler has been deprecated`)
         },
 
         /* to be overwritten by atlasViewer.component.ts */
         getToastHandler : () => {
-          if (this.getToastHandler) return this.getToastHandler()
-          else throw new Error('getToast Handler not overwritten by atlasViewer.component.ts')
+          throw new Error('uiHandle.getToastHandler has been deprecated')
         },
 
         /**
@@ -451,8 +420,8 @@ export interface IInteractiveViewerInterface {
   }
 
   uiHandle: {
-    getModalHandler: () => ModalHandler
-    getToastHandler: () => ToastHandler
+    getModalHandler: () => void
+    getToastHandler: () => void
     launchNewWidget: (manifest: IPluginManifest) => Promise<any>
     getUserInput: (config: IGetUserInputConfig) => Promise<string>
     getUserConfirmation: (config: IGetUserConfirmation) => Promise<any>
diff --git a/src/atlasViewer/atlasViewer.workerService.service.ts b/src/atlasViewer/atlasViewer.workerService.service.ts
index 17204cb05224dc78887f74cdae74ab9699ba6ec8..98dea256450eaddbcd4a0849176185317a5fe9bc 100644
--- a/src/atlasViewer/atlasViewer.workerService.service.ts
+++ b/src/atlasViewer/atlasViewer.workerService.service.ts
@@ -1,6 +1,7 @@
 import { Injectable } from "@angular/core";
 import { fromEvent } from "rxjs";
 import { filter, take } from "rxjs/operators";
+import { getUuid } from "src/util/fn";
 
 /* telling webpack to pack the worker file */
 import '../util/worker.js'
@@ -24,7 +25,7 @@ export class AtlasWorkerService {
 
   async sendMessage(data: IWorkerMessage){
 
-    const newUuid = crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
+    const newUuid = getUuid()
     this.worker.postMessage({
       id: newUuid,
       ...data
diff --git a/src/atlasViewer/modalUnit/modalUnit.component.ts b/src/atlasViewer/modalUnit/modalUnit.component.ts
deleted file mode 100644
index eb62a94bb383b8a17e0a3d9d145872e23b0cede0..0000000000000000000000000000000000000000
--- a/src/atlasViewer/modalUnit/modalUnit.component.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Component, Input, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core'
-
-@Component({
-  templateUrl : './modalUnit.template.html',
-  styleUrls : [
-    './modalUnit.style.css',
-  ],
-})
-
-export class ModalUnit {
-  @Input() public title: string
-  @Input() public body: string = 'Modal Body Text'
-  @Input() public template: TemplateRef<any>
-  @Input() public footer: string
-
-  @ViewChild('templateContainer', {read: ViewContainerRef}) public templateContainer: ViewContainerRef
-
-  constructor(public viewContainerRef: ViewContainerRef) {
-
-  }
-
-  public ngAfterViewInit() {
-    if (this.templateContainer) {
-      this.templateContainer.createEmbeddedView(this.template)
-    }
-  }
-}
diff --git a/src/atlasViewer/modalUnit/modalUnit.style.css b/src/atlasViewer/modalUnit/modalUnit.style.css
deleted file mode 100644
index 619233185073476c359eb2b6188f1a5280a166c2..0000000000000000000000000000000000000000
--- a/src/atlasViewer/modalUnit/modalUnit.style.css
+++ /dev/null
@@ -1,8 +0,0 @@
-:host-context(.darktheme) div.modal-header,
-:host-context(.darktheme) div.modal-body,
-:host-context(.darktheme) div.modal-footer
-{
-  background-color:rgba(50,50,50,1.0);
-  border-color: rgba(0,0,0,0.2);
-  color:white;
-}
\ No newline at end of file
diff --git a/src/atlasViewer/modalUnit/modalUnit.template.html b/src/atlasViewer/modalUnit/modalUnit.template.html
deleted file mode 100644
index a3aba585c53b1d35e044ec5aa92ece92c94a05a2..0000000000000000000000000000000000000000
--- a/src/atlasViewer/modalUnit/modalUnit.template.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<div *ngIf = "title" class = "modal-header">
-  <h4 class = "modal-title pull-left">
-    {{ title }}
-  </h4>
-</div>
-
-<div *ngIf = "!template" class = "modal-body">
-  {{ body }}
-</div>
-
-<div *ngIf = "template" class="modal-body">
-  <ng-template #templateContainer>
-
-  </ng-template>
-</div>
-
-<div *ngIf = "footer" class = "modal-footer">
-  {{ footer }}
-</div>
\ No newline at end of file
diff --git a/src/main.module.ts b/src/main.module.ts
index a87d7e6d8fa662263b04876717cbcd98a0c480b0..11a705ee245213a857e933cd3c82dce5010beb9b 100644
--- a/src/main.module.ts
+++ b/src/main.module.ts
@@ -1,6 +1,6 @@
 import { DragDropModule } from '@angular/cdk/drag-drop'
 import { CommonModule } from "@angular/common";
-import { CUSTOM_ELEMENTS_SCHEMA, InjectionToken, NgModule } from "@angular/core";
+import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";
 import { FormsModule } from "@angular/forms";
 import { StoreModule, ActionReducer } from "@ngrx/store";
 import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module'
@@ -14,9 +14,8 @@ import { GetNamesPipe } from "./util/pipes/getNames.pipe";
 
 import { HttpClientModule } from "@angular/common/http";
 import { EffectsModule } from "@ngrx/effects";
-import { AtlasViewerAPIServices, CANCELLABLE_DIALOG, GET_TOAST_HANDLER_TOKEN, API_SERVICE_SET_VIEWER_HANDLE_TOKEN, setViewerHandleFactory, LOAD_MESH_TOKEN, ILoadMesh } from "./atlasViewer/atlasViewer.apiService.service";
+import { AtlasViewerAPIServices, CANCELLABLE_DIALOG, API_SERVICE_SET_VIEWER_HANDLE_TOKEN, setViewerHandleFactory, LOAD_MESH_TOKEN, ILoadMesh } from "./atlasViewer/atlasViewer.apiService.service";
 import { AtlasWorkerService } from "./atlasViewer/atlasViewer.workerService.service";
-import { ModalUnit } from "./atlasViewer/modalUnit/modalUnit.component";
 import { TransformOnhoverSegmentPipe } from "./atlasViewer/onhoverSegment.pipe";
 import { ConfirmDialogComponent } from "./components/confirmDialog/confirmDialog.component";
 import { DialogComponent } from "./components/dialog/dialog.component";
@@ -121,7 +120,6 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
   ],
   declarations : [
     AtlasViewer,
-    ModalUnit,
     TryMeComponent,
 
     /* directives */
@@ -140,7 +138,6 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
     MouseOverIconPipe,
   ],
   entryComponents : [
-    ModalUnit,
     DialogComponent,
     ConfirmDialogComponent,
   ],
@@ -153,13 +150,6 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
     UIService,
     TemplateCoordinatesTransformation,
     ClickInterceptorService,
-    {
-      provide: GET_TOAST_HANDLER_TOKEN,
-      useFactory: (uiService: UIService) => {
-        return () => uiService.getToastHandler()
-      },
-      deps: [ UIService ]
-    },
     {
       provide: OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN,
       useFactory: (glue: IDatasetPreviewGlue) => glue.displayDatasetPreview.bind(glue),
diff --git a/src/plugin_examples/migrationGuide.md b/src/plugin_examples/migrationGuide.md
index 1489d5b848ab70382b63575190309020b0f8c148..9cd95c535efd5699d03ed3d04e2d57afc5ad554a 100644
--- a/src/plugin_examples/migrationGuide.md
+++ b/src/plugin_examples/migrationGuide.md
@@ -49,16 +49,4 @@ Plugin APIs have changed drastically from v0.1.0 to v0.2.0. Here is a list of pl
   - ~~*onParcellationSelection(callback)* : Function that attach a callback function to user selecting a different parcellation~~ removed. use **window.interactiveViewer.metadata.selectedParcellationBSubject** instead.
   - ~~*afterParcellationSelection(callback)* : Function that attach a callback function to be called after the parcellation selection process is complete and *selectedParcellation* is updated.~~ removed
   - *modalControl*
-    - *getModalHandler()* : Function returning a handler to change/show/hide/listen to a Modal. 
-    - *modalHander* methods:
-      - *hide()* : Dynamically hides the modal
-      - *show()* : Shows the modal
-      - *onHide(callback(reason)=>void)* : Attaches an onHide callback. 
-      - *onHidden(callback(reason)=>void)* : Attaches an onHidden callback. 
-      - *onShow(callback(reason)=>void)* : Attaches an onShow callback. 
-      - *onShown(callback(reason)=>void)* : Attaches an onShown callback.
-    - *modalHandler* properties:
-      - title : title of the modal (String)
-      - body : body of the modal shown (JSON, Array, String)
-      - footer : footer of the modal (String)
-      - config : config of the modal
\ No newline at end of file
+    - ~~*getModalHandler()* : Function returning a handler to change/show/hide/listen to a Modal.~~ removed
\ No newline at end of file
diff --git a/src/plugin_examples/plugin_api.md b/src/plugin_examples/plugin_api.md
index a8b29b30f48e239a0e2a1294c845eaa24abc0e8d..d9663435dfd2d544adf1ca7c6dbc5a0d9c3ef995 100644
--- a/src/plugin_examples/plugin_api.md
+++ b/src/plugin_examples/plugin_api.md
@@ -222,34 +222,6 @@ whether the modal is dismissable on click backdrop/esc key (Boolean)
 
 *n.b. if true, users will not be able to interact with the viewer unless you specifically call `handler.hide()`*
 
-#### getToastHandler()
-
-returns a toastHandler objectm, which has the following methods/properties:
-
-##### show()
-
-Show the toast
-
-##### hide()
-
-Dynamically hides the toast
-
-##### message
-
-message on the toast
-
-##### htmlMessage
-
-HTML message. If used to display user content, beware of script injection. Angular strips `style` attribute, so use `class` and bootstrap for styling.
-
-##### dismissable
-
-allow user dismiss the toast via x 
-
-##### timeout
-
-auto hide (in ms). set to 0 for not auto hide.
-
 #### launchNewWidget(manifest)
 
 returns a Promise. expects a JSON object, with the same key value as a plugin manifest. the *name* key must be unique, or the promise will be rejected. 
diff --git a/src/services/uiService.service.ts b/src/services/uiService.service.ts
index dc6f4dda7ea8c427536f2b567e94ef2b24e726ab..cc8774728fb1742e36722135960341c35f7c0599 100644
--- a/src/services/uiService.service.ts
+++ b/src/services/uiService.service.ts
@@ -1,5 +1,4 @@
 import { Injectable } from "@angular/core";
-import { ToastHandler } from "src/util/pluginHandlerClasses/toastHandler";
 import {MatSnackBar, MatSnackBarConfig} from "@angular/material/snack-bar";
 import { MatDialog } from "@angular/material/dialog";
 import { ActionDialog } from "src/ui/actionDialog/actionDialog.component";
@@ -15,22 +14,6 @@ export class UIService {
   ) {
   }
 
-  public getToastHandler = () => {
-    const toasthandler = new ToastHandler()
-    let handle
-    toasthandler.show = () => {
-      handle = this.showMessage(toasthandler.message, null, {
-        duration: toasthandler.timeout,
-      })
-    }
-
-    toasthandler.hide = () => {
-      if (handle) { handle.dismiss() }
-      handle = null
-    }
-    return toasthandler
-  }
-
   public showMessage(message: string, actionBtnTxt: string = 'Dismiss', config?: Partial<MatSnackBarConfig>) {
     return this.snackbar.open(message, actionBtnTxt, config)
   }
diff --git a/src/theme.scss b/src/theme.scss
index b2b1c43ca50117e48bfc37e5b27ddbf507686d6b..85ecba40b9d80b41f5692c7b9a4ea88bd195d075 100644
--- a/src/theme.scss
+++ b/src/theme.scss
@@ -1,4 +1,5 @@
 @import '~@angular/material/theming';
+@import '~@angular/cdk/overlay-prebuilt.css';
 
 @include  mat-core();
 
diff --git a/src/ui/screenshot/index.ts b/src/ui/screenshot/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9bb724742f69501cfd9d0b330b56b32294f38e4e
--- /dev/null
+++ b/src/ui/screenshot/index.ts
@@ -0,0 +1,5 @@
+export { ScreenshotModule } from './module'
+export {
+  HANDLE_SCREENSHOT_PROMISE,
+  TypeHandleScrnShotPromise,
+} from './util'
diff --git a/src/ui/screenshot/module.ts b/src/ui/screenshot/module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..68ca809f2ca6d93d53263f25d5ffa20430318c8b
--- /dev/null
+++ b/src/ui/screenshot/module.ts
@@ -0,0 +1,28 @@
+import { FullscreenOverlayContainer, OverlayContainer } from "@angular/cdk/overlay";
+import { CommonModule } from "@angular/common";
+import { NgModule } from "@angular/core";
+import { UtilModule } from "src/util";
+import { AngularMaterialModule } from "../sharedModules/angularMaterial.module";
+import { ScreenshotCmp } from "./screenshotCmp/screenshot.component";
+import { ScreenshotSwitch } from "./screenshotSwitch.directive";
+
+@NgModule({
+  imports: [
+    CommonModule,
+    AngularMaterialModule,
+    UtilModule,
+  ],
+  declarations:[
+    ScreenshotSwitch,
+    ScreenshotCmp,
+  ],
+  exports: [
+    ScreenshotSwitch,
+  ],
+  providers:[{
+    provide: OverlayContainer,
+    useClass: FullscreenOverlayContainer
+  }]
+})
+
+export class ScreenshotModule{}
diff --git a/src/ui/screenshot/screenshotCmp/screenshot.component.ts b/src/ui/screenshot/screenshotCmp/screenshot.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9af9d6b90a7d60af3438b2784906918b6f74aab0
--- /dev/null
+++ b/src/ui/screenshot/screenshotCmp/screenshot.component.ts
@@ -0,0 +1,200 @@
+import { Component, HostListener, Inject, OnDestroy, Output, EventEmitter, Optional, ViewChild, TemplateRef } from "@angular/core";
+import { MatDialog } from "@angular/material/dialog";
+import { MatSnackBar } from "@angular/material/snack-bar";
+import { combineLatest, Observable, Subject, Subscription } from "rxjs";
+import { distinctUntilChanged, map, mapTo, shareReplay, startWith, switchMap, switchMapTo, tap } from "rxjs/operators";
+import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "../util";
+import { getDateString } from 'common/util'
+import { getUuid } from "src/util/fn";
+
+const BORDER_WIDTH = 10000
+
+@Component({
+  templateUrl: './screenshot.template.html',
+  styleUrls: [
+    './screenshot.style.css'
+  ]
+})
+
+export class ScreenshotCmp implements OnDestroy{
+
+  public borderStyle = `${BORDER_WIDTH}px rgba(0, 0, 0, 0.5) solid`
+  private mousedown$ = new Subject<MouseEvent>()
+  private mouseup$ = new Subject<MouseEvent>()
+  private mousemove$ = new Subject<MouseEvent>()
+  private subscriptions: Subscription[] = []
+
+  public isDragging$: Observable<boolean>
+  public transformString$: Observable<string>
+  public widthString$: Observable<string>
+  public heightString$: Observable<string>
+
+  private flipRect$: Observable<{ x: boolean, y: boolean }>
+
+  constructor(
+    private dialog: MatDialog,
+    private snackbar: MatSnackBar,
+    @Optional() @Inject(HANDLE_SCREENSHOT_PROMISE) private handleScreenshotPromise: TypeHandleScrnShotPromise
+  ){
+    this.isDragging$ = this.mousedown$.pipe(
+      switchMapTo(this.mouseup$.pipe(
+        mapTo(false),
+        startWith(true)
+      )),
+      distinctUntilChanged(),
+    )
+
+    this.flipRect$ = this.mousedown$.pipe(
+      switchMap(mousedownEv => this.mousemove$.pipe(
+        map(mousemoveEv => {
+          return {
+            x: mousemoveEv.clientX < mousedownEv.clientX,
+            y: mousemoveEv.clientY < mousedownEv.clientY
+          }
+        })
+      )),
+      distinctUntilChanged(({ x: oldX, y: oldY }, {x, y}) => oldX === x && oldY === y),
+    )
+
+    this.transformString$ = combineLatest([
+      this.mousedown$,
+      this.flipRect$,
+    ]).pipe(
+      map(([ev, { x, y }]) => {
+        /**
+         * scale will retroactively change the effect of previous translations
+         */
+        const xFactor = x ? -1 : 1
+        const yFactor = y ? -1 : 1
+        return `translate(${-BORDER_WIDTH * xFactor}px, ${-BORDER_WIDTH * yFactor}px) scale(${xFactor}, ${yFactor}) translate(${(ev.clientX) * (xFactor)}px, ${(ev.clientY) * (yFactor)}px)`
+      }),
+      shareReplay(1),
+    )
+
+    this.subscriptions.push(
+      this.transformString$.subscribe()
+    )
+
+    const width$ = this.mousedown$.pipe(
+      switchMap(ev => this.mousemove$.pipe(
+        map(moveEv => Math.abs(moveEv.clientX - ev.clientX)),
+        startWith(0),
+      )),
+      shareReplay(1),
+    )
+
+    this.widthString$ = width$.pipe(
+      map(width => `${width}px`),
+      shareReplay(1),
+    )
+
+    this.subscriptions.push(
+      this.widthString$.subscribe()
+    )
+
+    const height$ = this.mousedown$.pipe(
+      switchMap(ev => this.mousemove$.pipe(
+        map(moveEv => Math.abs(moveEv.clientY - ev.clientY)),
+        startWith(0),
+      )),
+      shareReplay(1),
+    )
+
+    this.heightString$ = height$.pipe(
+      map(height => `${height}px`),
+      shareReplay(1),
+    )
+
+    this.subscriptions.push(
+      this.heightString$.subscribe()
+    )
+
+    this.subscriptions.push(
+      combineLatest([
+        this.mousedown$,
+        this.flipRect$,
+        width$,
+        height$,
+      ]).pipe(
+        switchMap(arg => this.mouseup$.pipe(
+          mapTo(arg)
+        ))
+      ).subscribe(([ startEv, flipRect, width, height ]) => {
+        const startX = startEv.clientX
+        const startY = startEv.clientY
+        if (!handleScreenshotPromise) {
+          console.warn(`HANDLE_SCREENSHOT_PROMISE not provided`)
+          return
+        }
+
+        if (width < 5 || height < 5) {
+          snackbar.open('width and height needs to be a bit larger', null, {
+            duration: 1000
+          })
+          return
+        }
+
+        this.captureScreenshot({
+          x: flipRect.x ? startX - width : startX,
+          y: flipRect.y ? startY - height : startY,
+          width,
+          height
+        })
+      })
+    )
+  }
+
+  captureScreenshot(param?){
+
+    this.handleScreenshotPromise(param)
+      .then(({ revoke, url }) => {
+        this.dialog.open(
+          this.previewTmpl,
+          {
+            data: {
+              url,
+              download: `${getDateString()}_${getUuid()}.png`
+            }
+          }
+        ).afterClosed().subscribe(
+          reason => {
+            /**
+             * if user clicks outside, or clicks cancel, emit destroy signal
+             */
+            if (!reason || reason === 'cancel') {
+              this.destroy.emit()
+            }
+            revoke()
+          }
+        )
+      })
+      .catch(e => {
+        this.snackbar.open(e, 'Dismiss')
+      })
+  }
+
+  ngOnDestroy(){
+    while(this.subscriptions.length > 0) this.subscriptions.pop().unsubscribe()
+  }
+
+  @HostListener('mousedown', ['$event'])
+  mousedown(ev: MouseEvent){
+    this.mousedown$.next(ev)
+  }
+
+  @HostListener('mouseup', ['$event'])
+  mouseup(ev: MouseEvent){
+    this.mouseup$.next(ev)
+  }
+
+  @HostListener('mousemove', ['$event'])
+  mousemove(ev: MouseEvent){
+    this.mousemove$.next(ev)
+  }
+
+  @Output()
+  destroy = new EventEmitter()
+
+  @ViewChild('previewTmpl', { read: TemplateRef })
+  private previewTmpl: TemplateRef<any>
+}
\ No newline at end of file
diff --git a/src/ui/screenshot/screenshotCmp/screenshot.style.css b/src/ui/screenshot/screenshotCmp/screenshot.style.css
new file mode 100644
index 0000000000000000000000000000000000000000..052e938e4b62e7c5eb2ab8a1fac99dca19be16b8
--- /dev/null
+++ b/src/ui/screenshot/screenshotCmp/screenshot.style.css
@@ -0,0 +1,30 @@
+:host
+{
+  display: block;
+  width:100%;
+}
+
+.cover
+{
+  background-color: rgba(0,0,0, 0.5);
+}
+
+.box
+{
+  /* background: none; */
+  box-sizing: content-box;
+  width: 0;
+  height: 0;
+  transform-origin: top left;
+}
+
+.inner-box
+{
+  border: 1px white solid;
+}
+
+img
+{
+  max-width: 50vw;
+  min-width: 20vw;
+}
\ No newline at end of file
diff --git a/src/ui/screenshot/screenshotCmp/screenshot.template.html b/src/ui/screenshot/screenshotCmp/screenshot.template.html
new file mode 100644
index 0000000000000000000000000000000000000000..ad7143b67c6c49b75f8341450b70633e997149eb
--- /dev/null
+++ b/src/ui/screenshot/screenshotCmp/screenshot.template.html
@@ -0,0 +1,74 @@
+
+<ng-template #placeholderTmpl>
+
+  <div class="d-flex align-items-center justify-content-center w-100 h-100 cover">
+    <span class="iv-custom-comp text">
+      <h2 class="mat-h2 text-center">
+        <span>
+          Drag a box to take a screenshot or
+        </span>
+
+        <button mat-stroked-button iav-stop="mousedown mouseup"
+          (click)="captureScreenshot()">
+          <i class="fas fa-camera"></i>
+          <span class="ml-1">
+            capture whole screen
+          </span>
+        </button>
+      </h2>
+
+      <h3 class="mat-h3 text-center">
+        <span>
+          cancel with Esc or
+        </span>
+        <button iav-stop="mousedown mouseup"
+          (click)="destroy.emit()"
+          mat-button>
+          click here
+        </button>
+      </h3>
+    </span>
+  </div>
+
+</ng-template>
+
+<ng-template [ngIf]="isDragging$ | async" [ngIfElse]="placeholderTmpl">
+  <div [style.border]="borderStyle"
+    [style.transform]="(transformString$ | async)"
+    [style.width]="widthString$ | async"
+    [style.height]="heightString$ | async"
+    class="box">
+    <div class="inner-box h-100 w-100">
+    </div>
+  </div>
+</ng-template>
+
+
+<ng-template #previewTmpl let-data>
+  
+  <mat-dialog-content class="d-flex justify-content-center">
+    <img [src]="data.url | safeResource">
+  </mat-dialog-content>
+
+  <mat-dialog-actions align="end">
+    <a [href]="data.url | safeResource"
+      [download]="data.download"
+      mat-raised-button color="primary">
+      <i class="fas fa-save"></i>
+      <span class="ml-1">Save</span>
+    </a>
+
+    <button mat-stroked-button
+      color="default"
+      mat-dialog-close="try again">
+      <i class="fas fa-camera"></i>
+      <span class="ml-1">Try again</span>
+    </button>
+
+    <button mat-button
+      color="default"
+      mat-dialog-close="cancel">
+      Cancel
+    </button>
+  </mat-dialog-actions>
+</ng-template>
\ No newline at end of file
diff --git a/src/ui/screenshot/screenshotSwitch.directive.ts b/src/ui/screenshot/screenshotSwitch.directive.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e80be541b86313b80cd4c13e15eb7e5d31164dc8
--- /dev/null
+++ b/src/ui/screenshot/screenshotSwitch.directive.ts
@@ -0,0 +1,67 @@
+import { Overlay, OverlayRef } from "@angular/cdk/overlay";
+import { ComponentPortal } from "@angular/cdk/portal";
+import { ComponentRef, Directive, HostListener } from "@angular/core";
+import { take } from "rxjs/operators";
+import { ScreenshotCmp } from "./screenshotCmp/screenshot.component";
+
+@Directive({
+  selector: '[screenshot-switch]'
+})
+
+export class ScreenshotSwitch{
+  public takingScreenshot = false
+
+  private overlayRef: OverlayRef
+  private cmpRef: ComponentRef<ScreenshotCmp>
+
+  @HostListener('window:keydown', ['$event'])
+  keyListener(ev: KeyboardEvent){
+    if (ev.key === 'Escape') {
+      if (this.overlayRef) this.dispose()
+    }
+  }
+
+  @HostListener('click')
+  onClick(){
+    /**
+     * button should act as a toggle?
+     */
+    
+    this.overlayRef = this.overlay.create({
+      hasBackdrop: false,
+      positionStrategy: this.overlay.position().global().centerVertically(),
+      panelClass: ['w-100', 'h-100'],
+    })
+
+    this.cmpRef = this.overlayRef.attach(
+      new ComponentPortal(ScreenshotCmp)
+    )
+    
+    this.cmpRef.instance.destroy.pipe(
+      take(1)
+    ).subscribe(
+      () => {
+        this.dispose()
+      }
+    )
+    
+    
+  }
+
+  dispose(){
+    this.cmpRef = null
+    if (this.overlayRef) this.overlayRef.dispose()
+    this.overlayRef = null
+  }
+
+  reset(){
+    this.takingScreenshot = true
+  }
+
+  clear(){
+    this.takingScreenshot = false
+  }
+
+  constructor(private overlay: Overlay){}
+
+}
\ No newline at end of file
diff --git a/src/ui/screenshot/util.ts b/src/ui/screenshot/util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5a84dc7f1b76e9a420c3d311d1b7dfdd34e42439
--- /dev/null
+++ b/src/ui/screenshot/util.ts
@@ -0,0 +1,14 @@
+import { InjectionToken } from "@angular/core"
+
+interface IScrnShot{
+  x: number
+  y: number
+  width: number
+  height: number
+}
+
+/**
+ * if param is not provided, screenshot entire screen
+ */
+export type TypeHandleScrnShotPromise = (param?: IScrnShot) => Promise<{ revoke: Function, url: string }>
+export const HANDLE_SCREENSHOT_PROMISE = new InjectionToken('HANDLE_SCREENSHOT_PROMISE')
\ No newline at end of file
diff --git a/src/ui/signinBanner/signinBanner.components.ts b/src/ui/signinBanner/signinBanner.components.ts
index 9c823d35fbd4ee77f8ff656b43f2b87e16ff2816..ef5c4e442357fcd684657bb9fb503888d11d5e30 100644
--- a/src/ui/signinBanner/signinBanner.components.ts
+++ b/src/ui/signinBanner/signinBanner.components.ts
@@ -2,10 +2,8 @@ import {
   ChangeDetectionStrategy,
   ChangeDetectorRef,
   Component,
-  ElementRef,
   Input,
   TemplateRef,
-  ViewChild
 } from "@angular/core";
 import { select, Store } from "@ngrx/store";
 import { Observable } from "rxjs";
@@ -47,15 +45,12 @@ export class SigninBanner {
   @Input() public darktheme: boolean
   @Input() public parcellationIsSelected: boolean
 
-  @ViewChild('takeScreenshotElement', {read: ElementRef}) takeScreenshotElement: ElementRef
-
   public user$: Observable<any>
   public userBtnTooltip$: Observable<string>
   public favDataEntries$: Observable<Partial<IDataEntry>[]>
 
   public pluginTooltipText: string = `Plugins and Tools`
   public screenshotTooltipText: string = 'Take screenshot'
-  public takingScreenshot: boolean = false
 
   constructor(
     private store$: Store<IavRootStoreInterface>,
@@ -92,17 +87,6 @@ export class SigninBanner {
     }
   }
 
-  resetScreenshotTaking() {
-    this.takingScreenshot = true
-    //ToDo find out better way to detect changes
-    setTimeout(() => this.changeDetectionRef.detectChanges())
-  }
-
-  disableScreenshotTaking() {
-    this.takingScreenshot = false
-    this.changeDetectionRef.detectChanges()
-  }
-
   private keyListenerConfigBase = {
     type: 'keydown',
     stop: true,
diff --git a/src/ui/signinBanner/signinBanner.style.css b/src/ui/signinBanner/signinBanner.style.css
index 787a6e42e83b544a36a33d1f634480724c5baeda..d1021184f2955c2dd4721ee4eb86731ff32f57ee 100644
--- a/src/ui/signinBanner/signinBanner.style.css
+++ b/src/ui/signinBanner/signinBanner.style.css
@@ -10,11 +10,6 @@
   pointer-events: all;
 }
 
-take-screenshot
-{
-  z-index: 1509;
-}
-
 .btnWrapper
 {
   margin: 0.5rem 0;
diff --git a/src/ui/signinBanner/signinBanner.template.html b/src/ui/signinBanner/signinBanner.template.html
index ae9fe7663c001f81f708fc4f296fdc5a691a42d4..c4b925620a6ec51a92bb4293bda290706bd8f6c5 100644
--- a/src/ui/signinBanner/signinBanner.template.html
+++ b/src/ui/signinBanner/signinBanner.template.html
@@ -139,11 +139,13 @@
   [aria-label]="'Tools and plugins menu'">
   <button mat-menu-item
     [disabled]="!parcellationIsSelected"
-    (click)="takingScreenshot = true;"
+    screenshot-switch
     [matTooltip]="screenshotTooltipText">
     <mat-icon fontSet="fas" fontIcon="fa-camera">
     </mat-icon>
-    Screenshot
+    <span>
+      Screenshot
+    </span>
   </button>
   <plugin-banner></plugin-banner>
 </mat-menu>
@@ -254,11 +256,3 @@
     </mat-list-item>
   </mat-list>
 </ng-template>
-
-<take-screenshot
-  #takeScreenshotElement
-  *ngIf="takingScreenshot"
-  class="position-fixed fixed-top"
-  (screenshotTaking)="disableScreenshotTaking()"
-  (resetScreenshot)="$event? resetScreenshotTaking() : disableScreenshotTaking()">
-</take-screenshot>
diff --git a/src/ui/takeScreenshot/takeScreenshot.component.ts b/src/ui/takeScreenshot/takeScreenshot.component.ts
deleted file mode 100644
index b10f94a06cee887ece6d331b661ec137af58a15d..0000000000000000000000000000000000000000
--- a/src/ui/takeScreenshot/takeScreenshot.component.ts
+++ /dev/null
@@ -1,285 +0,0 @@
-import {DOCUMENT} from "@angular/common";
-import {
-  ChangeDetectorRef,
-  Component,
-  ElementRef, EventEmitter,
-  HostListener,
-  Inject, OnDestroy,
-  OnInit, Output,
-  Renderer2,
-  TemplateRef,
-  ViewChild,
-} from "@angular/core";
-import {MatDialog, MatDialogRef} from "@angular/material/dialog";
-import html2canvas from "html2canvas";
-
-@Component({
-  selector: 'take-screenshot',
-  templateUrl: './takeScreenshot.template.html',
-  styleUrls: ['./takeScreenshot.style.css'],
-})
-
-export class TakeScreenshotComponent implements OnInit, OnDestroy {
-
-  ngOnDestroy(): void {
-    if (this.resettingScreenshotTaking) this.resetScreenshot.emit(true)
-    this.resettingScreenshotTaking = false
-  }
-
-    @ViewChild('screenshotPreviewCard', {read: ElementRef}) public screenshotPreviewCard: ElementRef
-    @ViewChild('previewImageDialog', {read: TemplateRef}) public previewImageDialogTemplateRef: TemplateRef<any>
-
-    @Output() screenshotTaking: EventEmitter<boolean> = new EventEmitter<boolean>()
-    @Output() resetScreenshot: EventEmitter<boolean> = new EventEmitter<boolean>()
-
-    private resettingScreenshotTaking: boolean = false
-
-    private dialogRef: MatDialogRef<any>
-
-    public takingScreenshot: boolean = false
-    public previewingScreenshot: boolean = false
-    public loadingScreenshot: boolean = false
-
-    public screenshotName: string = `screenshot.png`
-    private croppedCanvas = null
-
-    public mouseIsDown = false
-    public isDragging = false
-
-    // Used to calculate where to start showing the dragging area
-    private startX: number = 0
-    private startY: number = 0
-    private endX: number = 0
-    private endY: number = 0
-
-    public borderWidth: string = ''
-    // The box that contains the border and all required numbers.
-    public boxTop: number = 0
-    public boxLeft: number = 0
-    public boxEndWidth: number = 0
-    public boxEndHeight: number = 0
-
-    private windowHeight: number = 0
-    private windowWidth: number = 0
-
-    private screenshotStartX: number = 0
-    private screenshotStartY: number = 0
-
-    public imageUrl: string
-
-    constructor(
-        private renderer: Renderer2,
-        @Inject(DOCUMENT) private document: any,
-        private matDialog: MatDialog,
-        private cdr: ChangeDetectorRef,
-    ) {}
-
-    public ngOnInit(): void {
-      this.windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
-      this.windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
-
-      this.startScreenshot()
-
-    }
-
-    @HostListener('window:resize', ['$event'])
-    public onResize() {
-      this.windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
-      this.windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
-    }
-
-    @HostListener('window:keyup', ['$event'])
-    public keyEvent(event: KeyboardEvent) {
-      if (this.takingScreenshot && event.key === 'Escape') {
-        this.cancelTakingScreenshot()
-      }
-    }
-
-    public startScreenshot() {
-      this.previewingScreenshot = false
-      this.croppedCanvas = null
-      this.loadingScreenshot = false
-      this.takingScreenshot = true
-    }
-
-    public move(e: MouseEvent) {
-      if (this.mouseIsDown) {
-        this.isDragging = true
-
-        this.endY = e.clientY
-        this.endX = e.clientX
-
-        if (this.endX >= this.startX && this.endY >= this.startY) {
-          // III quadrant
-          this.borderWidth = this.startY + 'px '
-                    + (this.windowWidth - this.endX) + 'px '
-                    + (this.windowHeight - this.endY) + 'px '
-                    + this.startX + 'px'
-          this.boxTop = this.startY
-          this.boxLeft = this.startX
-          this.boxEndWidth = this.endX - this.startX
-          this.boxEndHeight = this.endY - this.startY
-
-          this.screenshotStartX = this.startX
-          this.screenshotStartY = this.startY
-
-        } else if (this.endX <= this.startX && this.endY >= this.startY) {
-          // IV quadrant
-
-          this.borderWidth = this.startY + 'px '
-                    + (this.windowWidth - this.startX) + 'px '
-                    + (this.windowHeight - this.endY) + 'px '
-                    + this.endX + 'px'
-
-          this.boxLeft = this.endX
-          this.boxTop = this.startY
-          this.boxEndWidth = this.startX - this.endX
-          this.boxEndHeight = this.endY - this.startY
-
-          this.screenshotStartX = this.endX
-          this.screenshotStartY = this.startY
-
-        } else if (this.endX >= this.startX && this.endY <= this.startY) {
-
-          // II quadrant
-
-          this.borderWidth = this.endY + 'px '
-                    + (this.windowWidth - this.endX) + 'px '
-                    + (this.windowHeight - this.startY) + 'px '
-                    + this.startX + 'px'
-
-          this.boxLeft = this.startX
-          this.boxTop = this.endY
-          this.boxEndWidth = this.endX - this.startX
-          this.boxEndHeight = this.startY - this.endY
-
-          this.screenshotStartX = this.startX
-          this.screenshotStartY = this.endY
-
-        } else if (this.endX <= this.startX && this.endY <= this.startY) {
-          // I quadrant
-
-          this.boxLeft = this.endX
-          this.boxTop = this.endY
-          this.boxEndWidth = this.startX - this.endX
-          this.boxEndHeight = this.startY - this.endY
-
-          this.borderWidth = this.endY + 'px '
-                    + (this.windowWidth - this.startX) + 'px '
-                    + (this.windowHeight - this.startY) + 'px '
-                    + this.endX + 'px'
-
-          this.screenshotStartX = this.endX
-          this.screenshotStartY = this.endY
-
-        } else {
-          this.isDragging = false
-        }
-
-      }
-    }
-
-    public mouseDown(event: MouseEvent) {
-      this.borderWidth = this.windowWidth + 'px ' + this.windowHeight + 'px'
-
-      this.startX = event.clientX
-      this.startY = event.clientY
-
-      this.mouseIsDown = true
-    }
-
-    public mouseUp(_event: MouseEvent) {
-      (window as any).viewer.display.update()
-      this.borderWidth = '0'
-
-      this.isDragging = false
-      this.mouseIsDown = false
-
-      this.takingScreenshot = false
-
-      if (this.boxEndWidth * window.devicePixelRatio <= 1 && this.boxEndHeight * window.devicePixelRatio <= 1) {
-        this.cancelTakingScreenshot()
-      } else {
-        this.loadScreenshot()
-      }
-
-    }
-
-    public loadScreenshot() {
-
-      this.loadingScreenshot = true
-      this.dialogRef = this.matDialog.open(this.previewImageDialogTemplateRef, {
-        autoFocus: false,
-      })
-      this.dialogRef.afterClosed().toPromise()
-        .then(result => {
-          switch (result) {
-          case 'again': {
-            this.restartScreenshot()
-            this.startScreenshot()
-            this.cdr.markForCheck()
-            break
-          }
-          case 'cancel': {
-            this.cancelTakingScreenshot()
-            break
-          }
-          default: this.cancelTakingScreenshot()
-          }
-        })
-
-      html2canvas(this.document.querySelector('#neuroglancer-container canvas')).then(canvas => {
-        this.croppedCanvas = null
-        this.croppedCanvas = this.renderer.createElement('canvas')
-
-        this.croppedCanvas.width = this.boxEndWidth * window.devicePixelRatio
-        this.croppedCanvas.height = this.boxEndHeight * window.devicePixelRatio
-
-        this.croppedCanvas.getContext('2d')
-          .drawImage(canvas,
-            this.screenshotStartX * window.devicePixelRatio, this.screenshotStartY * window.devicePixelRatio,
-            this.boxEndWidth * window.devicePixelRatio, this.boxEndHeight * window.devicePixelRatio,
-            0, 0,
-            this.boxEndWidth * window.devicePixelRatio, this.boxEndHeight * window.devicePixelRatio)
-      }).then(() => {
-
-        const d = new Date()
-        const n = `${d.getFullYear()}_${d.getMonth() + 1}_${d.getDate()}_${d.getHours()}_${d.getMinutes()}_${d.getSeconds()}`
-        this.screenshotName = `${n}_IAV.png`
-
-        this.loadingScreenshot = false
-        this.imageUrl = this.croppedCanvas.toDataURL('image/png')
-        this.previewingScreenshot = true
-        this.clearStateAfterScreenshot()
-
-        this.cdr.markForCheck()
-      })
-    }
-
-    public restartScreenshot() {
-      this.resettingScreenshotTaking = true
-      this.resetScreenshot.emit(false)
-    }
-
-    public cancelTakingScreenshot() {
-      this.screenshotTaking.emit(false)
-    }
-
-    public clearStateAfterScreenshot() {
-      this.mouseIsDown = false
-      this.isDragging = false
-      this.startX = 0
-      this.startY = 0
-      this.endX = 0
-      this.endY = 0
-      this.borderWidth = ''
-      this.boxTop = 0
-      this.boxLeft = 0
-      this.boxEndWidth = 0
-      this.boxEndHeight = 0
-      this.windowHeight = 0
-      this.windowWidth = 0
-      this.screenshotStartX = 0
-      this.screenshotStartY = 0
-    }
-}
diff --git a/src/ui/takeScreenshot/takeScreenshot.style.css b/src/ui/takeScreenshot/takeScreenshot.style.css
deleted file mode 100644
index f104726c32b3c6a08edb1bc04f005a7fa25ccb4a..0000000000000000000000000000000000000000
--- a/src/ui/takeScreenshot/takeScreenshot.style.css
+++ /dev/null
@@ -1,37 +0,0 @@
-.overlay,
-.tooltip,
-.borderedBox {
-    user-select: none;
-}
-
-.overlay {
-    background-color: rgba(0, 0, 0, 0.5);
-}
-
-.overlay.highlighting {
-    background: none;
-    border-color: rgba(0, 0, 0, 0.5);
-    border-style: solid;
-}
-
-.screenshotContainer {
-    clear: both;
-    background-repeat: no-repeat;
-    background-size: cover;
-
-    transition: opacity ease-in-out 200ms;
-}
-
-.smallSizeWindow {
-    height: 40px;
-}
-
-.cancelTimesPosition {
-    top: 5px;
-    right: -10px;
-}
-
-.screenshotPreviewCard {
-    z-index: 10520 !important;
-    background:none;
-}
\ No newline at end of file
diff --git a/src/ui/takeScreenshot/takeScreenshot.template.html b/src/ui/takeScreenshot/takeScreenshot.template.html
deleted file mode 100644
index edbdcfa45775b870299274dc5f205b9902116a97..0000000000000000000000000000000000000000
--- a/src/ui/takeScreenshot/takeScreenshot.template.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<div class="screenshotContainer overflow-hidden w-50 h-50"
-    (mousemove)="move($event)"
-    (mousedown)="mouseDown($event)"
-    (mouseup)="mouseUp($event)"
-    [ngClass]="{'pe-none o-0':!takingScreenshot, 'o-1': takingScreenshot}"
-    [ngStyle]="{'cursor':takingScreenshot? 'crosshair' : 'auto'}">
-
-    <div class="overlay position-fixed fixed-top w-100 h-100 d-flex align-items-center justify-content-center"
-        [ngClass]="{ 'highlighting' : mouseIsDown }"
-        [ngStyle]="{ borderWidth: borderWidth }">
-
-        <!-- instruction text -->
-        <mat-card *ngIf="!isDragging" class="screenshotPreviewCard pe-none">
-            <mat-card-title>
-                Drag a box to take a screenshot
-            </mat-card-title>
-            <mat-card-subtitle class="text-muted d-flex justify-content-center">
-                cancel with Esc
-            </mat-card-subtitle>
-        </mat-card>
-    </div>
-    <div class="position-absolute border border-light"
-        *ngIf="isDragging"
-        [ngStyle]="{ left: boxLeft + 'px', top: boxTop + 'px', width: boxEndWidth + 'px', height: boxEndHeight + 'px' }">
-    </div>
-</div>
-
-<ng-template #previewImageDialog>
-    <mat-dialog-content>
-
-        <div class="d-flex w-100 h-100 justify-content-center align-items-center" *ngIf="loadingScreenshot">
-            <span class="text-nowrap">Generating screenshot </span> <i class="fas fa-spinner fa-pulse ml-1"></i>
-        </div>
-        <ng-template [ngIf]="!loadingScreenshot">
-            <img [src]="imageUrl" class="w-100 h-100">
-        </ng-template>
-    </mat-dialog-content>
-    <mat-dialog-actions align="end">
-
-        <a *ngIf="imageUrl"
-            [href]="imageUrl"
-            [download]="screenshotName">
-
-            <button mat-raised-button
-                color="primary"
-                class="mr-2">
-                <i class="fas fa-save"></i> Save
-            </button>
-        </a>
-        <button mat-stroked-button
-            color="default"
-            class="mr-2"
-            mat-dialog-close="again">
-            <i class="fas fa-camera"></i> Try again
-        </button>
-        <button mat-button
-            color="default"
-            mat-dialog-close="cancel">
-            Cancel
-        </button>
-    </mat-dialog-actions>
-</ng-template>
\ No newline at end of file
diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts
index b45831e8f461f2ca53a9a51c17cdc0b65340016d..e58bd77ba1c134fad099b3c439e73b425f7cc1c4 100644
--- a/src/ui/ui.module.ts
+++ b/src/ui/ui.module.ts
@@ -56,7 +56,6 @@ import { ReorderPanelIndexPipe } from "./nehubaContainer/reorderPanelIndex.pipe"
 import { TouchSideClass } from "./nehubaContainer/touchSideClass.directive";
 import { BinSavedRegionsSelectionPipe, SavedRegionsSelectionBtnDisabledPipe } from "./viewerStateController/viewerState.pipes";
 
-import { TakeScreenshotComponent } from "src/ui/takeScreenshot/takeScreenshot.component";
 import { FixedMouseContextualContainerDirective } from "src/util/directives/FixedMouseContextualContainerDirective.directive";
 import { RegionHierarchy } from './viewerStateController/regionHierachy/regionHierarchy.component'
 import { RegionTextSearchAutocomplete } from "./viewerStateController/regionSearch/regionSearch.component";
@@ -86,6 +85,7 @@ import { HelpOnePager } from "./helpOnePager/helpOnePager.component";
 import { RegionalFeaturesModule } from "./regionalFeatures";
 import { Landmark2DModule } from "./nehubaContainer/2dLandmarks/module";
 import { PluginCspCtrlCmp } from "./config/pluginCsp/pluginCsp.component";
+import { ScreenshotModule, HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "./screenshot";
 
 @NgModule({
   imports : [
@@ -106,6 +106,7 @@ import { PluginCspCtrlCmp } from "./config/pluginCsp/pluginCsp.component";
     FabSpeedDialModule,
     RegionalFeaturesModule,
     Landmark2DModule,
+    ScreenshotModule,
   ],
   declarations : [
     NehubaContainer,
@@ -138,7 +139,6 @@ import { PluginCspCtrlCmp } from "./config/pluginCsp/pluginCsp.component";
     RegionHierarchy,
     MaximmisePanelButton,
     RegionTextSearchAutocomplete,
-    TakeScreenshotComponent,
     RegionMenuComponent,
     ConnectivityBrowserComponent,
     SimpleRegionComponent,
@@ -189,6 +189,60 @@ import { PluginCspCtrlCmp } from "./config/pluginCsp/pluginCsp.component";
       provide: APPEND_SCRIPT_TOKEN,
       useFactory: appendScriptFactory,
       deps: [ DOCUMENT ]
+    },
+    {
+      provide: HANDLE_SCREENSHOT_PROMISE,
+      useValue: ((param) => {
+        const canvas: HTMLCanvasElement = document.querySelector('#neuroglancer-container canvas')
+        if (!canvas) return Promise.reject(`element '#neuroglancer-container canvas' not found`)
+        const _ = (window as any).viewer.display.draw()
+        if (!param) {
+          return new Promise(rs => {
+            canvas.toBlob(blob => {
+              const url = URL.createObjectURL(blob)
+              rs({
+                url,
+                revoke: () => URL.revokeObjectURL(url)
+              })
+            }, 'image/png')
+          })
+        }
+        const { x, y, width, height } = param
+        const { devicePixelRatio: dpr } = window
+        return new Promise(rs => {
+          const subCanvas = document.createElement('canvas')
+          subCanvas.width = width * dpr
+          subCanvas.height = height * dpr
+          const context = subCanvas.getContext('2d')
+          context.drawImage(
+            canvas,
+
+            /**
+             * from
+             */
+            x * dpr,
+            y * dpr,
+            width * dpr,
+            height * dpr,
+
+            /**
+             * to
+             */
+            0,
+            0,
+            width * dpr,
+            height * dpr
+          )
+
+          subCanvas.toBlob(blob => {
+            const url = URL.createObjectURL(blob)
+            rs({
+              url,
+              revoke: () => URL.revokeObjectURL(url)
+            })
+          }, 'image/png')
+        })
+      }) as TypeHandleScrnShotPromise
     }
   ],
   entryComponents : [
diff --git a/src/util/fn.ts b/src/util/fn.ts
index 9307ae0930cc410525a9bfebb05b0a5fe1196d22..827aec4f0e872df3433aacc123066f6707ce2f72 100644
--- a/src/util/fn.ts
+++ b/src/util/fn.ts
@@ -51,3 +51,7 @@ export function recursiveFindRegionWithLabelIndexId({ regions, labelIndexId, inh
   if (found) { return found }
   return null
 }
+
+export function getUuid(){
+  return crypto.getRandomValues(new Uint32Array(1))[0].toString(16)
+}
diff --git a/src/util/pluginHandlerClasses/modalHandler.ts b/src/util/pluginHandlerClasses/modalHandler.ts
deleted file mode 100644
index 2afa36774891ced56a784d685f02dd159570ec58..0000000000000000000000000000000000000000
--- a/src/util/pluginHandlerClasses/modalHandler.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-export class ModalHandler {
-
-  public hide: () => void
-  public show: () => void
-  // onHide : (callback: () => void) => void
-  // onHidden : (callback : () => void) => void
-  // onShow : (callback : () => void) => void
-  // onShown : (callback : () => void) => void
-  public title: string
-  public body: string
-  public footer: string
-
-  public dismissable = true
-}
diff --git a/src/util/pluginHandlerClasses/toastHandler.ts b/src/util/pluginHandlerClasses/toastHandler.ts
deleted file mode 100644
index 9bba88be2bd00bc936ba0da7609054ddc5ea769d..0000000000000000000000000000000000000000
--- a/src/util/pluginHandlerClasses/toastHandler.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export class ToastHandler {
-  public message = 'Toast message'
-  public timeout = 3000
-  public dismissable = true
-  public show: () => void
-  public hide: () => void
-  public htmlMessage: string
-}