diff --git a/angular.json b/angular.json
index af94adeb567bf3ab3406beeba94e8191e7e97c11..dcbadb4911f1203a6204541cc20e00b2ec00947b 100644
--- a/angular.json
+++ b/angular.json
@@ -65,7 +65,9 @@
               "input": "third_party/vanilla_nehuba.js",
               "inject": false,
               "bundleName": "vanilla_nehuba"
-            },{
+            },
+            
+            {
               "input": "export-nehuba/dist/min/main.bundle.js",
               "inject": false,
               "bundleName": "main.bundle"
@@ -73,7 +75,22 @@
               "input": "export-nehuba/dist/min/chunk_worker.bundle.js",
               "inject": false,
               "bundleName": "chunk_worker.bundle"
+            },
+            {
+              "input": "export-nehuba/dist/min/draco.bundle.js",
+              "inject": false,
+              "bundleName": "draco.bundle"
             },{
+              "input": "export-nehuba/dist/min/async_computation.bundle.js",
+              "inject": false,
+              "bundleName": "async_computation.bundle"
+            },{
+              "input": "export-nehuba/dist/min/blosc.bundle.js",
+              "inject": false,
+              "bundleName": "blosc.bundle"
+            },
+            
+            {
               "inject": false,
               "input": "third_party/leap-0.6.4.js",
               "bundleName": "leap-0.6.4"
diff --git a/docs/releases/v2.12.0.md b/docs/releases/v2.12.0.md
index d6c600ed49b32102affd2d6a34e027dc68a73c16..d06017301861d4aec9d2f6dd809bc016632b6fba 100644
--- a/docs/releases/v2.12.0.md
+++ b/docs/releases/v2.12.0.md
@@ -7,3 +7,4 @@
 ## Behind the scene
 
 - update spotlight mechanics from in-house to angular CDK
+- Updated neuroglancer/nehuba dependency. This allows volumes with non-rigid affine to be displayed properly.
diff --git a/package-lock.json b/package-lock.json
index 792fff775330a11e4a0c83b26158e1694e7836f7..8e7f6b6a39ea268baf93a705910d5bae30c429ea 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "siibra-explorer",
-  "version": "2.11.2",
+  "version": "2.12.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "siibra-explorer",
-      "version": "2.11.2",
+      "version": "2.12.0",
       "license": "apache-2.0",
       "dependencies": {
         "@angular/animations": "^14.2.12",
@@ -23,7 +23,7 @@
         "@ngrx/effects": "^14.3.2",
         "@ngrx/store": "^14.3.2",
         "acorn": "^8.4.1",
-        "export-nehuba": "0.0.12",
+        "export-nehuba": "^0.1.0-dev.8",
         "file-loader": "^6.2.0",
         "jszip": "^3.6.0",
         "postcss": "^8.3.6",
@@ -26966,9 +26966,9 @@
       "dev": true
     },
     "node_modules/export-nehuba": {
-      "version": "0.0.12",
-      "resolved": "https://registry.npmjs.org/export-nehuba/-/export-nehuba-0.0.12.tgz",
-      "integrity": "sha512-pf3hAwpXaOqlfBfgmPLYQ+uLqJ+ElyvE1bDrrCrf5Qf0Otsekw+8CcyAJhP5O15Yacmhe7Py3G96tw5bbvZyIA==",
+      "version": "0.1.0-dev.8",
+      "resolved": "https://registry.npmjs.org/export-nehuba/-/export-nehuba-0.1.0-dev.8.tgz",
+      "integrity": "sha512-nSiGMclXztCG5N9tNL9xf1Q8Eq2WN2FwtjJpXEtUxsIj/rzVWdU0EkEHjv6APDNTbDA9kgU2Nc61HLqcWWdIgQ==",
       "dependencies": {
         "pako": "^1.0.6"
       }
diff --git a/package.json b/package.json
index 9414f748771618ee48197f0ef0e3e58c898c0f0c..e0c290ded11056934fb23cf9ec04454531840cf8 100644
--- a/package.json
+++ b/package.json
@@ -66,7 +66,7 @@
     "@ngrx/effects": "^14.3.2",
     "@ngrx/store": "^14.3.2",
     "acorn": "^8.4.1",
-    "export-nehuba": "0.0.12",
+    "export-nehuba": "^0.1.0-dev.8",
     "file-loader": "^6.2.0",
     "jszip": "^3.6.0",
     "postcss": "^8.3.6",
diff --git a/src/atlasComponents/annotations/annotation.service.ts b/src/atlasComponents/annotations/annotation.service.ts
index 3969bc420cdea13c69fef90bbc765de4b047e0de..f0a63f7aa298c0cad46efcd96a1dfbe452f1098b 100644
--- a/src/atlasComponents/annotations/annotation.service.ts
+++ b/src/atlasComponents/annotations/annotation.service.ts
@@ -167,9 +167,6 @@ export class AnnotationLayer {
   }
 
   private parseNgSpecType(spec: AnnotationSpec): _AnnotationSpec{
-    const voxelSize = this.viewer.navigationState.voxelSize.toJSON()
-    const sanitizePoint = (p: [number, number, number]) => p.map((v, idx) => v / voxelSize[idx]) as [number, number, number]
-    const needSanitizePosition = voxelSize[0] !== 1 || voxelSize[1] !== 1 || voxelSize[2] !== 1
     const overwrite: Partial<_AnnotationSpec> = {}
     switch (spec.type) {
     case "point": {
@@ -187,15 +184,6 @@ export class AnnotationLayer {
     default: throw new Error(`overwrite type lookup failed for ${(spec as any).type}`)
     }
 
-    /**
-     * The unit of annotation(s) depends on voxel size. If it is 1,1,1 then it would be in um, but often it is not.
-     * If not sanitized, the annotation can be miles off.
-     */
-    if (needSanitizePosition) {
-      for (const key of ['point', 'pointA', 'pointB'] ) {
-        if (!!spec[key]) overwrite[key] = sanitizePoint(spec[key])
-      }
-    }
     return {
       ...spec,
       ...overwrite,
diff --git a/src/messagingGlue.ts b/src/messagingGlue.ts
index ae415e68cff96abaaf964e6606a2a93df8cbc782..994bf30d8aab393eccfa769dc1ac17f1c5fcf8ce 100644
--- a/src/messagingGlue.ts
+++ b/src/messagingGlue.ts
@@ -86,7 +86,8 @@ export class MessagingGlue implements IWindowMessaging, OnDestroy {
           "1"
         ],
         transform: transform,
-        clType: 'customlayer/nglayer' as const
+        clType: 'customlayer/nglayer' as const,
+        type: 'segmentation',
       }
 
       this.store.dispatch(
diff --git a/src/state/atlasAppearance/const.ts b/src/state/atlasAppearance/const.ts
index 41c9ebd7216465719aab792025b8ad2b5b268f6d..9804bc43091e3350d89ab2f0d028ba889f15d738 100644
--- a/src/state/atlasAppearance/const.ts
+++ b/src/state/atlasAppearance/const.ts
@@ -31,7 +31,7 @@ export type NgLayerCustomLayer = {
   transform?: number[][]
   opacity?: number
   segments?: (number|string)[]
-  // type?: string
+  type?: string
 
   // annotation?: string // TODO what is this used for?
 } & CustomLayerBase
diff --git a/src/state/atlasSelection/effects.spec.ts b/src/state/atlasSelection/effects.spec.ts
index fe99266715ac18a342335b5b46591f29c2140444..9ba912441b896aaf519a14893496e1db2beb9a27 100644
--- a/src/state/atlasSelection/effects.spec.ts
+++ b/src/state/atlasSelection/effects.spec.ts
@@ -158,13 +158,13 @@ describe("> effects.ts", () => {
                 },
                 previous: {
                   atlas: {
-                    "@id": IDS.ATLAES.RAT
+                    id: IDS.ATLAES.RAT
                   } as any,
                   parcellation: {
-                    "@id": IDS.PARCELLATION.WAXHOLMV4
+                    id: IDS.PARCELLATION.WAXHOLMV4
                   } as any,
                   template: {
-                    "@id": IDS.TEMPLATES.WAXHOLM
+                    id: IDS.TEMPLATES.WAXHOLM
                   } as any,
                 }
               })
@@ -186,24 +186,24 @@ describe("> effects.ts", () => {
               const obs = hook({
                 current: {
                   atlas: {
-                    "@id": IDS.ATLAES.HUMAN
+                    id: IDS.ATLAES.HUMAN
                   } as any,
                   parcellation: {
-                    "@id": IDS.PARCELLATION.JBA29
+                    id: IDS.PARCELLATION.JBA29
                   } as any,
                   template: {
-                    "@id": IDS.TEMPLATES.MNI152
+                    id: IDS.TEMPLATES.MNI152
                   } as any,
                 },
                 previous: {
                   atlas: {
-                    "@id": IDS.ATLAES.RAT
+                    id: IDS.ATLAES.RAT
                   } as any,
                   parcellation: {
-                    "@id": IDS.PARCELLATION.WAXHOLMV4
+                    id: IDS.PARCELLATION.WAXHOLMV4
                   } as any,
                   template: {
-                    "@id": IDS.TEMPLATES.WAXHOLM
+                    id: IDS.TEMPLATES.WAXHOLM
                   } as any,
                 }
               })
diff --git a/src/state/atlasSelection/effects.ts b/src/state/atlasSelection/effects.ts
index 816c23c2439b953c511d15bc6168348b1a00c2db..5967e510255e45ba3eb1c980d7cf65a53018c429 100644
--- a/src/state/atlasSelection/effects.ts
+++ b/src/state/atlasSelection/effects.ts
@@ -63,19 +63,17 @@ export class Effect {
         })
       }
 
-      /**
-       * if either space name is undefined, return default state for navigation
-       */
-      if (!prevSpcName || !currSpcName) {
-        return of({
-          navigation: atlasSelection.defaultState.navigation
-        })
-      }
       return this.store.pipe(
         select(atlasSelection.selectors.navigation),
         take(1),
         switchMap(({ position, ...rest }) => 
-          this.interSpaceCoordXformSvc.transform(prevSpcName, currSpcName, position as [number, number, number]).pipe(
+        
+          /**
+           * if either space name is undefined, return default state for navigation
+           */
+          !prevSpcName || !currSpcName
+          ? of({ navigation: { position, ...rest } })
+          : this.interSpaceCoordXformSvc.transform(prevSpcName, currSpcName, position as [number, number, number]).pipe(
             map(value => {
               if (value.status === "error") {
                 return {}
diff --git a/src/util/fn.ts b/src/util/fn.ts
index 6457ccf78a80061da34734ae0c3b2e20b0ffe238..a859bad0898788e5c06c1e8292ddf2c66cafb452 100644
--- a/src/util/fn.ts
+++ b/src/util/fn.ts
@@ -1,18 +1,6 @@
 import { interval, Observable, of } from 'rxjs'
 import { filter, mapTo, take } from 'rxjs/operators'
 
-export function getViewer() {
-  return (window as any).viewer
-}
-
-export function setViewer(viewer) {
-  (window as any).viewer = viewer
-}
-
-export function setNehubaViewer(nehubaViewer) {
-  (window as any).nehubaViewer = nehubaViewer
-}
-
 export function getDebug() {
   return (window as any).__DEBUG__
 }
diff --git a/src/util/periodic.service.ts b/src/util/periodic.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e3e280cbc541f9a891dc6d7fd36f54dbb5ef385d
--- /dev/null
+++ b/src/util/periodic.service.ts
@@ -0,0 +1,57 @@
+import { Injectable } from "@angular/core";
+import { Subject, combineLatest, interval, merge } from "rxjs";
+import { filter, map, scan } from "rxjs/operators";
+import { getUuid } from "./fn";
+
+type Queue = {
+  callback: () => boolean
+  uuid: string
+}
+
+@Injectable({
+  providedIn: 'root'
+})
+export class PeriodicSvc{
+  #queue$ = new Subject<Queue>()
+  #dequeue$ = new Subject<Queue>()
+  #scannedQueue$ = merge<{ queue?: Queue, dequeue?: Queue }>(
+    this.#queue$.pipe(
+      map(queue => ({ queue })),
+    ),
+    this.#dequeue$.pipe(
+      map(dequeue => ({ dequeue }))
+    )
+  ).pipe(
+    scan((acc, curr) => {
+      const { queue, dequeue } = curr
+      if (queue) {
+        return [...acc, queue]
+      }
+      if (dequeue) {
+        return acc.filter(q => q.uuid !== dequeue.uuid)
+      }
+      console.warn(`neither queue nor dequeue were defined!`)
+      return acc
+    }, [] as Queue[])
+  )
+
+  addToQueue(callback: () => boolean){
+    this.#queue$.next({ callback, uuid: getUuid() })
+  }
+  constructor(){
+    combineLatest([
+      this.#scannedQueue$,
+      interval(160)
+    ]).pipe(
+      map(([queues, _]) => queues),
+      filter(queues => queues.length > 0),
+    ).subscribe(queues => {
+      for (const queue of queues) {
+        const { callback } = queue
+        if (callback()) {
+          this.#dequeue$.next(queue)
+        }
+      }
+    })
+  }
+}
diff --git a/src/viewerModule/nehuba/config.service/util.ts b/src/viewerModule/nehuba/config.service/util.ts
index ddc82c6482332f12aeb7adef4013e51bb442217f..2ef5a1cc94d1602c6106ac34e17b983a766e33d1 100644
--- a/src/viewerModule/nehuba/config.service/util.ts
+++ b/src/viewerModule/nehuba/config.service/util.ts
@@ -8,6 +8,7 @@ import {
   RecursivePartial,
 } from "./type"
 import { translateV3Entities } from "src/atlasComponents/sapi/translateV3"
+import { PERSPECTIVE_ZOOM_FUDGE_FACTOR } from "../constants"
 // fsaverage uses threesurfer, which, whilst do not use ngId, uses 'left' and 'right' as keys 
 const fsAverageKeyVal = {
   [IDS.PARCELLATION.JBA29]: {
@@ -374,8 +375,8 @@ export function getNehubaConfig(space: SxplrTemplate): NehubaConfig {
         "drawSubstrates": drawSubstrates,
         "drawZoomLevels": drawZoomLevels,
         "restrictZoomLevel": {
-          "minZoom": 1200000 * scale,
-          "maxZoom": 3500000 * scale
+          "minZoom": 1200000 * scale * PERSPECTIVE_ZOOM_FUDGE_FACTOR,
+          "maxZoom": 3500000 * scale * PERSPECTIVE_ZOOM_FUDGE_FACTOR
         }
       }
     }
diff --git a/src/viewerModule/nehuba/constants.ts b/src/viewerModule/nehuba/constants.ts
index 667c5b7147246a276e3a02f59cd436851db83932..d35b8393c26bbb08bbfccf2b67561200b778d006 100644
--- a/src/viewerModule/nehuba/constants.ts
+++ b/src/viewerModule/nehuba/constants.ts
@@ -35,3 +35,10 @@ export type TNehubaViewerUnit = {
 export const SET_MESHES_TO_LOAD = new InjectionToken<Observable<IMeshesToLoad>>('SET_MESHES_TO_LOAD')
 
 export const PMAP_LAYER_NAME = 'regional-pmap'
+
+/**
+ * since export_nehuba@0.1.0 onwards (the big update that changed a lot of neuroglancer's internals)
+ * there is now a multiplier bewteen old and new perspective views
+ * to maintain interop with previous states, translate the multiplier
+ */
+export const PERSPECTIVE_ZOOM_FUDGE_FACTOR = 82.842712474619
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts
index b8e5e4b26a8f59e6f0dce71eaf8ee227d6e46e80..a37e9608e7b8b96c48673a3d3043ecfb99688997 100644
--- a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts
@@ -75,7 +75,8 @@ export class LayerCtrlEffects {
                       highThreshold: meta.max,
                       lowThreshold: meta.min,
                       removeBg: true,
-                    })
+                    }),
+                    type: 'image'
                   }
                 })
               )
diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
index 18b8805b2eb9f6000918879eeb557fe307bffe69..ed281a389241e4a8fbaec5c004557601f2af1aa2 100644
--- a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
+++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.spec.ts
@@ -5,6 +5,7 @@ import { LoggingModule, LoggingService } from "src/logging"
 import { IMeshesToLoad, SET_MESHES_TO_LOAD } from "../constants"
 import { Subject } from "rxjs"
 import { IColorMap, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl.service"
+import { rgbToHex } from 'common/util'
 
 describe('> nehubaViewer.component.ts', () => {
   describe('> #scanFn', () => {
@@ -305,15 +306,42 @@ describe('> nehubaViewer.component.ts', () => {
 
     describe('> # setColorMap', () => {
       let nehubaViewerSpy: any
+      let ngViewerStatechildrenGetSpy = jasmine.createSpy('get')
+      let toJsonSpy = jasmine.createSpy('toJsonSpy')
+      let restoreStateSpy = jasmine.createSpy('restoreStateSpy')
+
+      const ngId1 = 'foo-bar'
+      const ngId2 = 'hello-world'
       beforeEach(() => {
         nehubaViewerSpy = {
-          batchAddAndUpdateSegmentColors: jasmine.createSpy(),
           dispose(){
 
+          },
+          ngviewer: {
+            state: {
+              children: {
+                get: ngViewerStatechildrenGetSpy
+              }
+            }
           }
         }
+
+        ngViewerStatechildrenGetSpy.and.returnValue({
+          toJSON: toJsonSpy,
+          restoreState: restoreStateSpy,
+        })
+        toJsonSpy.and.returnValue([{
+          name: ngId1
+        }, {
+          name: ngId2
+        }])
       })
-      it('> calls nehubaViewer.batchAddAndUpdateSegmentColors', () => {
+      afterEach(() => {
+        ngViewerStatechildrenGetSpy.calls.reset()
+        toJsonSpy.calls.reset()
+        restoreStateSpy.calls.reset()
+      })
+      it('> calls nehubaViewer.restoreState', () => {
         const fixture = TestBed.createComponent(NehubaViewerUnit)
         fixture.componentInstance.nehubaViewer = nehubaViewerSpy
         fixture.detectChanges()
@@ -322,28 +350,28 @@ describe('> nehubaViewer.component.ts', () => {
         const fooBarMap = new Map()
         fooBarMap.set(1, {red: 100, green: 100, blue: 100})
         fooBarMap.set(2, {red: 200, green: 200, blue: 200})
-        mainMap.set('foo-bar', fooBarMap)
+        mainMap.set(ngId1, fooBarMap)
 
         const helloWorldMap = new Map()
         helloWorldMap.set(1, {red: 10, green: 10, blue: 10})
         helloWorldMap.set(2, {red: 20, green: 20, blue: 20})
-        mainMap.set('hello-world', helloWorldMap)
+        mainMap.set(ngId2, helloWorldMap)
 
         fixture.componentInstance['setColorMap'](mainMap)
 
-        expect(
-          nehubaViewerSpy.batchAddAndUpdateSegmentColors
-        ).toHaveBeenCalledTimes(2)
-
-        expect(nehubaViewerSpy.batchAddAndUpdateSegmentColors).toHaveBeenCalledWith(
-          fooBarMap,
-          { name: 'foo-bar' }
-        )
-
-        expect(nehubaViewerSpy.batchAddAndUpdateSegmentColors).toHaveBeenCalledWith(
-          helloWorldMap,
-          { name: 'hello-world' }
-        )
+        expect(restoreStateSpy).toHaveBeenCalledOnceWith([{
+          name: ngId1,
+          segmentColors: {
+            1: rgbToHex([100, 100, 100]),
+            2: rgbToHex([200, 200, 200]),
+          }
+        }, {
+          name: ngId2,
+          segmentColors: {
+            1: rgbToHex([10, 10, 10]),
+            2: rgbToHex([20, 20, 20]),
+          }
+        }])
       })
     })
 
diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
index e0e25cd158aedca533bbe1b9a336832ff2f82298..1bdcedc1685bc6605bb95f56f9e881daebadf6f0 100644
--- a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
@@ -1,17 +1,27 @@
 import { Component, ElementRef, EventEmitter, OnDestroy, Output, Inject, Optional } from "@angular/core";
-import { Subscription, BehaviorSubject, Observable, Subject, of, interval } from 'rxjs'
-import { debounceTime, filter, scan, switchMap, take, distinctUntilChanged, debounce } from "rxjs/operators";
+import { Subscription, BehaviorSubject, Observable, Subject, of, interval, combineLatest } from 'rxjs'
+import { debounceTime, filter, scan, switchMap, take, distinctUntilChanged, debounce, map } from "rxjs/operators";
 import { LoggingService } from "src/logging";
-import { bufferUntil, getExportNehuba, getViewer, setNehubaViewer, switchMapWaitFor } from "src/util/fn";
+import { bufferUntil, getExportNehuba, switchMapWaitFor } from "src/util/fn";
 import { deserializeSegment, NEHUBA_INSTANCE_INJTKN } from "../util";
-import { arrayOrderedEql } from 'common/util'
-import { IMeshesToLoad, SET_MESHES_TO_LOAD } from "../constants";
+import { arrayOrderedEql, rgbToHex } from 'common/util'
+import { IMeshesToLoad, SET_MESHES_TO_LOAD, PERSPECTIVE_ZOOM_FUDGE_FACTOR } from "../constants";
 import { IColorMap, SET_COLORMAP_OBS, SET_LAYER_VISIBILITY } from "../layerCtrl.service";
 
 /**
  * import of nehuba js files moved to angular.json
  */
 import { INgLayerCtrl, NG_LAYER_CONTROL, SET_SEGMENT_VISIBILITY, TNgLayerCtrl } from "../layerCtrl.service/layerCtrl.util";
+import { NgCoordinateSpace, Unit } from "../types";
+import { PeriodicSvc } from "src/util/periodic.service";
+
+function translateUnit(unit: Unit) {
+  if (unit === "m") {
+    return 1e9
+  }
+
+  throw new Error(`Cannot translate unit: ${unit}`)
+}
 
 export const IMPORT_NEHUBA_INJECT_TOKEN = `IMPORT_NEHUBA_INJECT_TOKEN`
 
@@ -49,10 +59,11 @@ export const scanFn = (acc: LayerLabelIndex[], curr: LayerLabelIndex) => {
 
 export class NehubaViewerUnit implements OnDestroy {
 
+  #translateVoxelToReal: (voxels: number[]) => number[]
 
   public ngIdSegmentsMap: Record<string, number[]> = {}
 
-  public viewerPosInVoxel$ = new BehaviorSubject(null)
+  public viewerPosInVoxel$ = new BehaviorSubject<number[]>(null)
   public viewerPosInReal$ = new BehaviorSubject<[number, number, number]>(null)
   public mousePosInVoxel$ = new BehaviorSubject<[number, number, number]>(null)
   public mousePosInReal$ = new BehaviorSubject(null)
@@ -97,33 +108,18 @@ export class NehubaViewerUnit implements OnDestroy {
       : [1.5e9, 1.5e9, 1.5e9]
   }
 
-  public _s2$: any = null
-  public _s3$: any = null
-  public _s4$: any = null
-  public _s5$: any = null
-  public _s6$: any = null
-  public _s7$: any = null
-  public _s8$: any = null
-
-  public _s$: any[] = [
-    this._s2$,
-    this._s3$,
-    this._s4$,
-    this._s5$,
-    this._s6$,
-    this._s7$,
-    this._s8$,
-  ]
+  #newViewerSubs: { unsubscribe: () => void }[] = []
 
   public ondestroySubscriptions: Subscription[] = []
 
   public nehubaLoaded: boolean = false
 
-  public landmarksLoaded: boolean = false
+  #triggerMeshLoad$ = new BehaviorSubject(null)
 
   constructor(
     public elementRef: ElementRef,
     private log: LoggingService,
+    private periodicSvc: PeriodicSvc,
     @Inject(IMPORT_NEHUBA_INJECT_TOKEN) getImportNehubaPr: () => Promise<any>,
     @Optional() @Inject(NEHUBA_INSTANCE_INJTKN) private nehubaViewer$: Subject<NehubaViewerUnit>,
     @Optional() @Inject(SET_MESHES_TO_LOAD) private injSetMeshesToLoad$: Observable<IMeshesToLoad>,
@@ -152,9 +148,10 @@ export class NehubaViewerUnit implements OnDestroy {
         this.loadNehuba()
 
         const viewer = this.nehubaViewer.ngviewer
-        this.layersChangedHandler = viewer.layerManager.layersChanged.add(() => {
+
+        this.layersChangedHandler = viewer.layerManager.readyStateChanged.add(() => {
           this.layersChanged.emit(null)
-          const readiedLayerNames: string[] = viewer.layerManager.managedLayers.filter(l => l.layer).map(l => l.name)
+          const readiedLayerNames: string[] = viewer.layerManager.managedLayers.filter(l => l.isReady()).map(l => l.name)
           for (const layerName in this.ngIdSegmentsMap) {
             if (!readiedLayerNames.includes(layerName)) {
               return
@@ -291,9 +288,13 @@ export class NehubaViewerUnit implements OnDestroy {
 
     if (this.injSetMeshesToLoad$) {
       this.subscriptions.push(
-        this.injSetMeshesToLoad$.pipe(
-          scan(scanFn, []),
-          debounceTime(16),
+        combineLatest([
+          this.#triggerMeshLoad$,
+          this.injSetMeshesToLoad$.pipe(
+            scan(scanFn, []),
+          ),
+        ]).pipe(
+          map(([_, val]) => val),
           debounce(() => this._nehubaReady
             ? of(true)
             : interval(160).pipe(
@@ -325,14 +326,6 @@ export class NehubaViewerUnit implements OnDestroy {
     }
   }
 
-  public navPosReal: [number, number, number] = [0, 0, 0]
-  public navPosVoxel: [number, number, number] = [0, 0, 0]
-
-  public mousePosReal: [number, number, number] = [0, 0, 0]
-  public mousePosVoxel: [number, number, number] = [0, 0, 0]
-
-  public viewerState: ViewerState
-
   private _multiNgIdColorMap: Map<string, Map<number, {red: number, green: number, blue: number}>>
   get multiNgIdColorMap() {
     return this._multiNgIdColorMap
@@ -353,7 +346,9 @@ export class NehubaViewerUnit implements OnDestroy {
     this.nehubaViewer = this.exportNehuba.createNehubaViewer(this.config, (err: string) => {
       /* print in debug mode */
       this.log.error(err)
-    })
+    });
+
+    (window as any).nehubaViewer = this.nehubaViewer
 
     /**
      * Hide all layers except the base layer (template)
@@ -362,15 +357,15 @@ export class NehubaViewerUnit implements OnDestroy {
 
     /* creation of the layout is done on next frame, hence the settimeout */
     setTimeout(() => {
-      getViewer().display.panels.forEach(patchSliceViewPanel)
+      window['viewer'].display.panels.forEach(patchSliceViewPanel)
     })
 
     this.newViewerInit()
-    this.loadNewParcellation()
-
-    setNehubaViewer(this.nehubaViewer)
+    window['nehubaViewer'] = this.nehubaViewer
 
-    this.onDestroyCb.push(() => setNehubaViewer(null))
+    this.onDestroyCb.push(() => {
+      window['nehubaViewer'] = null
+    })
   }
 
   public ngOnDestroy() {
@@ -380,10 +375,10 @@ export class NehubaViewerUnit implements OnDestroy {
     while (this.subscriptions.length > 0) {
       this.subscriptions.pop().unsubscribe()
     }
-
-    this._s$.forEach(_s$ => {
-      if (_s$) { _s$.unsubscribe() }
-    })
+    while (this.#newViewerSubs.length > 0) {
+      this.#newViewerSubs.pop().unsubscribe()
+    }
+    
     this.ondestroySubscriptions.forEach(s => s.unsubscribe())
     while (this.onDestroyCb.length > 0) {
       this.onDestroyCb.pop()()
@@ -482,6 +477,27 @@ export class NehubaViewerUnit implements OnDestroy {
         /* if the layer exists, it will not be loaded */
         !viewer.layerManager.getLayerByName(key))
       .map(key => {
+        /**
+         * new implementation of neuroglancer treats swc as a mesh layer of segmentation layer
+         * But it cannot *directly* be accessed by nehuba's setMeshesToLoad, since it filters by 
+         * UserSegmentationLayer.
+         * 
+         * The below monkey patch sets the mesh to load, allow the SWC to be shown
+         */
+        const isSwc = layerObj[key]['source'].includes("swc://")
+        const hasSegment = (layerObj[key]["segments"] || []).length > 0
+        if (isSwc && hasSegment) {
+          this.periodicSvc.addToQueue(
+            () => {
+              const layer = viewer.layerManager.getLayerByName(key)
+              if (!(layer?.layer)) {
+                return false
+              }
+              layer.layer.displayState.visibleSegments.setMeshesToLoad([1])
+              return true
+            }
+          )
+        }
         viewer.layerManager.addManagedLayer(
           viewer.layerSpecification.getLayer(key, layerObj[key]))
 
@@ -509,7 +525,6 @@ export class NehubaViewerUnit implements OnDestroy {
           name: ngId,
         })
       }
-
       this.nehubaViewer.showSegment(0, {
         name: ngId,
       })
@@ -524,7 +539,6 @@ export class NehubaViewerUnit implements OnDestroy {
           name: ngId,
         })
       }
-
       this.nehubaViewer.hideSegment(0, {
         name: ngId,
       })
@@ -604,7 +618,7 @@ export class NehubaViewerUnit implements OnDestroy {
     } = newViewerState || {}
 
     if ( perspectiveZoom ) {
-      this.nehubaViewer.ngviewer.perspectiveNavigationState.zoomFactor.restoreState(perspectiveZoom)
+      this.nehubaViewer.ngviewer.perspectiveNavigationState.zoomFactor.restoreState(perspectiveZoom * PERSPECTIVE_ZOOM_FUDGE_FACTOR)
     }
     if ( zoom ) {
       this.nehubaViewer.ngviewer.navigationState.zoomFactor.restoreState(zoom)
@@ -620,18 +634,6 @@ export class NehubaViewerUnit implements OnDestroy {
     }
   }
 
-  public obliqueRotateX(amount: number) {
-    this.nehubaViewer.ngviewer.navigationState.pose.rotateRelative(this.vec3([0, 1, 0]), -amount / 4.0 * Math.PI / 180.0)
-  }
-
-  public obliqueRotateY(amount: number) {
-    this.nehubaViewer.ngviewer.navigationState.pose.rotateRelative(this.vec3([1, 0, 0]), amount / 4.0 * Math.PI / 180.0)
-  }
-
-  public obliqueRotateZ(amount: number) {
-    this.nehubaViewer.ngviewer.navigationState.pose.rotateRelative(this.vec3([0, 0, 1]), amount / 4.0 * Math.PI / 180.0)
-  }
-
   public toggleOctantRemoval(flag?: boolean) {
     const ctrl = this.nehubaViewer?.ngviewer?.showPerspectiveSliceViews
     if (!ctrl) {
@@ -642,13 +644,6 @@ export class NehubaViewerUnit implements OnDestroy {
       ? !ctrl.value
       : flag
     ctrl.restoreState(newVal)
-
-    if (this.landmarksLoaded) {
-      /**
-       * showPerspectSliceView -> ! meshTransparency
-       */
-      this.setMeshTransparency(!newVal)
-    }
   }
 
   private setLayerTransparency(layerName: string, alpha: number) {
@@ -688,29 +683,29 @@ export class NehubaViewerUnit implements OnDestroy {
   }
 
   private newViewerInit() {
+    
+    while (this.#newViewerSubs.length > 0) {
+      this.#newViewerSubs.pop().unsubscribe()
+    }
 
-    /* isn't this layer specific? */
-    /* TODO this is layer specific. need a way to distinguish between different segmentation layers */
-    this._s2$ = this.nehubaViewer.mouseOver.segment
-      .subscribe(({ segment, layer }) => {
+    this.#newViewerSubs.push(
+
+      /* isn't this layer specific? */
+      /* TODO this is layer specific. need a way to distinguish between different segmentation layers */
+      this.nehubaViewer.mouseOver.segment.subscribe(({ segment, layer }) => {
         this.mouseOverSegment = segment
         this.mouseOverLayer = { ...layer }
-      })
+      }),
 
-    if (this.initNav) {
-      this.setNavigationState(this.initNav)
-      this.initNav = null
-    }
-
-    this._s8$ = this.nehubaViewer.mouseOver.segment.subscribe(({segment: segmentId, layer }) => {
-      this.mouseoverSegmentEmitter.emit({
-        layer,
-        segmentId,
-      })
-    })
+      this.nehubaViewer.mouseOver.segment.subscribe(({segment: segmentId, layer }) => {
+        this.mouseoverSegmentEmitter.emit({
+          layer,
+          segmentId,
+        })
+      }),
 
-    // nehubaViewer.navigationState.all emits every time a new layer is added or removed from the viewer
-    this._s3$ = this.nehubaViewer.navigationState.all
+      // nehubaViewer.navigationState.all emits every time a new layer is added or removed from the viewer
+      this.nehubaViewer.navigationState.all
       .distinctUntilChanged((a, b) => {
         const {
           orientation: o1,
@@ -733,71 +728,99 @@ export class NehubaViewerUnit implements OnDestroy {
           [0, 1, 2].every(idx => p1[idx] === p2[idx]) &&
           z1 === z2
       })
-      .filter(() => !this.initNav)
+      /**
+       * somewhat another fudge factor
+       * navigationState.all occassionally emits slice zoom and perspective zoom that maeks no sense
+       * filter those out
+       * 
+       * TODO find out why, and perhaps inform pavel about this
+       */
+      .filter(val => !this.initNav && val?.perspectiveZoom > 10)
       .subscribe(({ orientation, perspectiveOrientation, perspectiveZoom, position, zoom }) => {
-        this.viewerState = {
-          orientation,
-          perspectiveOrientation,
-          perspectiveZoom,
-          zoom,
-          position,
-          positionReal : false,
-        }
 
         this.viewerPositionChange.emit({
           orientation : Array.from(orientation),
           perspectiveOrientation : Array.from(perspectiveOrientation),
-          perspectiveZoom,
+          perspectiveZoom: perspectiveZoom / PERSPECTIVE_ZOOM_FUDGE_FACTOR,
           zoom,
           position: Array.from(position),
           positionReal : true,
         })
-      })
+      }),
+
+      this.nehubaViewer.navigationState.position.inVoxels
+        .filter(v => typeof v !== 'undefined' && v !== null)
+        .subscribe((v: Float32Array) => {
+          const coordInVoxel = Array.from(v)
+          this.viewerPosInVoxel$.next(coordInVoxel)
+          if (this.#translateVoxelToReal) {
+            
+            const coordInReal = this.#translateVoxelToReal(coordInVoxel)
+            this.viewerPosInReal$.next(coordInReal as [number, number, number])
+          }
+        }),
 
-    this._s4$ = this.nehubaViewer.navigationState.position.inRealSpace
-      .filter(v => typeof v !== 'undefined' && v !== null)
-      .subscribe(v => {
-        this.navPosReal = Array.from(v) as [number, number, number]
-        this.viewerPosInReal$.next(Array.from(v) as [number, number, number])
-      })
-    this._s5$ = this.nehubaViewer.navigationState.position.inVoxels
-      .filter(v => typeof v !== 'undefined' && v !== null)
-      .subscribe(v => {
-        this.navPosVoxel = Array.from(v) as [number, number, number]
-        this.viewerPosInVoxel$.next(Array.from(v))
-      })
-    this._s6$ = this.nehubaViewer.mousePosition.inRealSpace
-      .filter(v => typeof v !== 'undefined' && v !== null)
-      .subscribe(v => {
-        this.mousePosReal = Array.from(v) as [number, number, number]
-        this.mousePosInReal$.next(Array.from(v))
-      })
-    this._s7$ = this.nehubaViewer.mousePosition.inVoxels
-      .filter(v => typeof v !== 'undefined' && v !== null)
-      .subscribe(v => {
-        this.mousePosVoxel = Array.from(v) as [number, number, number]
-        this.mousePosInVoxel$.next(Array.from(v) as [number, number, number] )
-      })
-  }
+      this.nehubaViewer.mousePosition.inVoxels
+        .filter((v: Float32Array) => typeof v !== 'undefined' && v !== null)
+        .subscribe((v: Float32Array) => {
+          const coordInVoxel = Array.from(v) as [number, number, number]
+          this.mousePosInVoxel$.next( coordInVoxel )
+          if (this.#translateVoxelToReal) {
+            
+            const coordInReal = this.#translateVoxelToReal(coordInVoxel)
+            this.mousePosInReal$.next( coordInReal )
+          }
+        }),
 
-  private loadNewParcellation() {
+    )
 
-    this._s$.forEach(_s$ => {
-      if (_s$) { _s$.unsubscribe() }
+    const coordSpListener = this.nehubaViewer.ngviewer.coordinateSpace.changed.add(() => {
+      const coordSp = this.nehubaViewer.ngviewer.coordinateSpace.value as NgCoordinateSpace
+      if (coordSp.valid) {
+        this.#translateVoxelToReal = (coordInVoxel: number[]) => {
+          return coordInVoxel.map((voxel, idx) => (
+            translateUnit(coordSp.units[idx])
+            * coordSp.scales[idx]
+            * voxel
+          ))
+        }
+      }
     })
+    this.nehubaViewer.ngviewer.registerDisposer(coordSpListener)
+
+    if (this.initNav) {
+      this.setNavigationState(this.initNav)
+      this.initNav = null
+    }
+    
   }
 
   private setColorMap(map: Map<string, Map<number, {red: number, green: number, blue: number}>>) {
     this.multiNgIdColorMap = map
+    const mainDict: Record<string, Record<number, string>> = {}
     for (const [ ngId, cMap ] of map.entries()) {
-      const nMap = new Map()
+      const nRecord: Record<number, string> = {}
       for (const [ key, cm ] of cMap.entries()) {
-        nMap.set(Number(key), cm)
+        nRecord[key] = rgbToHex([cm.red, cm.green, cm.blue])
+      }
+      mainDict[ngId] = nRecord
+
+      /**
+       * n.b.
+       * cannot restoreState on each individual layer
+       * it seems to create duplicated datasources, which eats memory, and wrecks opacity
+       */
+    }
+
+    const layersManager = this.nehubaViewer.ngviewer.state.children.get("layers")
+    const layerJson = layersManager.toJSON()
+    for (const layer of layerJson) {
+      if (layer.name in mainDict) {
+        layer['segmentColors'] = mainDict[layer.name]
       }
-      this.nehubaViewer.batchAddAndUpdateSegmentColors(
-        nMap,
-        { name : ngId })
     }
+    layersManager.restoreState(layerJson)
+    this.#triggerMeshLoad$.next(null)
   }
 }
 
@@ -828,51 +851,4 @@ export interface ViewerState {
   zoom: number
 }
 
-export const ICOSAHEDRON = `# vtk DataFile Version 2.0
-Converted using https://github.com/HumanBrainProject/neuroglancer-scripts
-ASCII
-DATASET POLYDATA
-POINTS 12 float
--525731.0 0.0 850651.0
-525731.0 0.0 850651.0
--525731.0 0.0 -850651.0
-525731.0 0.0 -850651.0
-0.0 850651.0 525731.0
-0.0 850651.0 -525731.0
-0.0 -850651.0 525731.0
-0.0 -850651.0 -525731.0
-850651.0 525731.0 0.0
--850651.0 525731.0 0.0
-850651.0 -525731.0 0.0
--850651.0 -525731.0 0.0
-POLYGONS 20 80
-3 1 4 0
-3 4 9 0
-3 4 5 9
-3 8 5 4
-3 1 8 4
-3 1 10 8
-3 10 3 8
-3 8 3 5
-3 3 2 5
-3 3 7 2
-3 3 10 7
-3 10 6 7
-3 6 11 7
-3 6 0 11
-3 6 1 0
-3 10 1 6
-3 11 0 9
-3 2 11 9
-3 5 2 9
-3 11 2 7`
-
-declare const TextEncoder
-
-export const _encoder = new TextEncoder()
-export const ICOSAHEDRON_VTK_URL = URL.createObjectURL( new Blob([ _encoder.encode(ICOSAHEDRON) ], {type : 'application/octet-stream'} ))
-
-export const FRAGMENT_MAIN_WHITE = `void main(){emitRGB(vec3(1.0,1.0,1.0));}`
-export const FRAGMENT_EMIT_WHITE = `emitRGB(vec3(1.0, 1.0, 1.0));`
-export const FRAGMENT_EMIT_RED = `emitRGB(vec3(1.0, 0.1, 0.12));`
 export const computeDistance = (pt1: [number, number], pt2: [number, number]) => ((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) ** 0.5
diff --git a/src/viewerModule/nehuba/types.ts b/src/viewerModule/nehuba/types.ts
index c7684e637dd9a156c722db3ca806b7f381171bab..aaa7016009cf34950b64b07bba5d40be49a8953e 100644
--- a/src/viewerModule/nehuba/types.ts
+++ b/src/viewerModule/nehuba/types.ts
@@ -13,3 +13,25 @@ export type TNehubaContextInfo = {
     regions: SxplrRegion[]
   }[]
 }
+
+export type Unit = 'm'
+type Bound = {
+  lowerBounds: Float64Array
+  upperBounds: Float64Array
+}
+type BBox = {
+  transform: Float64Array
+  box: Bound
+}
+
+export type NgCoordinateSpace = {
+  valid: boolean
+  rank: number
+  names: string[]
+  timestamps: number[]
+  ids: number[]
+  units: Unit[]
+  scales: Float64Array
+  boundingBoxes:BBox[]
+  bounds: Bound
+}
diff --git a/src/viewerModule/nehuba/userLayers/service.ts b/src/viewerModule/nehuba/userLayers/service.ts
index 3fe9d0a7db656354ed6217ebd13a0fd0354deae4..41d16e71bc3e20aa74f717d11ec8a598d13b3c42 100644
--- a/src/viewerModule/nehuba/userLayers/service.ts
+++ b/src/viewerModule/nehuba/userLayers/service.ts
@@ -77,6 +77,7 @@ export class UserLayerService implements OnDestroy {
         options: {
           segments: ["1"],
           transform: xform,
+          type: "segmentation"
         },
       }
     }
@@ -121,6 +122,7 @@ export class UserLayerService implements OnDestroy {
           lowThreshold: meta.min || 0,
           highThreshold: meta.max || 1,
         }),
+        type: 'image'
       },
     }
   }
diff --git a/src/viewerModule/nehuba/util.ts b/src/viewerModule/nehuba/util.ts
index c8d56c7e5ba037ab8366e4ca40d0066481146789..0af076efd9c18e061e1e9f539c2911445aae4c77 100644
--- a/src/viewerModule/nehuba/util.ts
+++ b/src/viewerModule/nehuba/util.ts
@@ -1,7 +1,6 @@
 import { InjectionToken } from '@angular/core'
 import { Observable, pipe } from 'rxjs'
 import { filter, scan, take } from 'rxjs/operators'
-import { getViewer } from 'src/util/fn'
 import { NehubaViewerUnit } from './nehubaViewer/nehubaViewer.component'
 import { userInterface } from 'src/state'
 
@@ -202,7 +201,7 @@ export const takeOnePipe = () => {
        *
        * 4 ???
        */
-      const panels = getViewer()['display']['panels']
+      const panels = window['viewer']['display']['panels']
       const panelEls = Array.from(panels).map(({ element }) => element)
 
       const identifySrcElement = (element: HTMLElement) => {
diff --git a/third_party/vanilla.html b/third_party/vanilla.html
index 38206ec40b244a8b3011c6850dc46aa41f047b2f..be223e1e38ab861e649e4c4666e894d0a48c6f8c 100644
--- a/third_party/vanilla.html
+++ b/third_party/vanilla.html
@@ -8,6 +8,7 @@
   
   <script src="main.bundle.js"></script>
   <link rel="stylesheet" href="vanilla_styles.css">
+  <link rel="stylesheet" href="main.css">
 </head>
 <body>
   <div id="neuroglancer-container"></div>
diff --git a/worker/worker.js b/worker/worker.js
index ab8cc83715853cc94fb09e4a2bfce4b5f2597b6a..314e0838032142545197a572ce968eb0320d7ed1 100644
--- a/worker/worker.js
+++ b/worker/worker.js
@@ -11,15 +11,6 @@ if (typeof self.importScripts === 'function')  self.importScripts('./worker-plot
 if (typeof self.importScripts === 'function')  self.importScripts('./worker-nifti.js')
 if (typeof self.importScripts === 'function')  self.importScripts('./worker-typedarray.js')
 
-/**
- * TODO migrate processing functionalities to other scripts
- * see worker-plotly.js
- */
-
-const validTypes = [
-  'GET_USERLANDMARKS_VTK',
-  'PROPAGATE_PARC_REGION_ATTR'
-]
 
 const VALID_METHOD = {
   PROCESS_PLOTLY: `PROCESS_PLOTLY`,
@@ -39,177 +30,10 @@ const VALID_METHODS = [
   VALID_METHOD.PROCESS_TYPED_ARRAY_RAW,
 ]
 
-const validOutType = [
-  'ASSEMBLED_USERLANDMARKS_VTK',
-]
-
-const getVertexHeader = (numVertex) => `POINTS ${numVertex} float`
-
-const getPolyHeader = (numPoly) => `POLYGONS ${numPoly} ${4 * numPoly}`
-
-const getLabelHeader = (numVertex) => `POINT_DATA ${numVertex}
-SCALARS label unsigned_char 1
-LOOKUP_TABLE none`
-
-//pos in nm
-const getIcoVertex = (pos, scale) => `-525731.0 0.0 850651.0
-525731.0 0.0 850651.0
--525731.0 0.0 -850651.0
-525731.0 0.0 -850651.0
-0.0 850651.0 525731.0
-0.0 850651.0 -525731.0
-0.0 -850651.0 525731.0
-0.0 -850651.0 -525731.0
-850651.0 525731.0 0.0
--850651.0 525731.0 0.0
-850651.0 -525731.0 0.0
--850651.0 -525731.0 0.0`
-  .split('\n')
-  .map(line =>
-    line
-      .split(' ')
-      .map((string, idx) => (Number(string) * (scale ? scale : 1) + pos[idx]).toString() )
-      .join(' ')
-    )
-  .join('\n')
-
-
-const getIcoPoly = (startingIdx) => `3 1 4 0
-3 4 9 0
-3 4 5 9
-3 8 5 4
-3 1 8 4
-3 1 10 8
-3 10 3 8
-3 8 3 5
-3 3 2 5
-3 3 7 2
-3 3 10 7
-3 10 6 7
-3 6 11 7
-3 6 0 11
-3 6 1 0
-3 10 1 6
-3 11 0 9
-3 2 11 9
-3 5 2 9
-3 11 2 7`
-  .split('\n')
-  .map((line) =>
-    line
-      .split(' ')
-      .map((v,idx) => idx === 0 ? v : (Number(v) + startingIdx).toString() )
-      .join(' ')
-    )
-  .join('\n')
-
-const getMeshVertex = (vertices) =>  vertices.map(vertex => vertex.join(' ')).join('\n')
-const getMeshPoly = (polyIndices, currentIdx) => polyIndices.map(triplet =>
-  '3 '.concat(triplet.map(index =>
-    index + currentIdx
-  ).join(' '))
-).join('\n')
-
-
 const encoder = new TextEncoder()
 
-const parseLmToVtk = (landmarks, scale) => {
-
-  const reduce = landmarks.reduce((acc,curr,idx) => {
-    //curr : null | [number,number,number] | [ [number,number,number], [number,number,number], [number,number,number] ][]
-    if(curr === null) return acc
-    if(!isNaN(curr[0]))
-      /**
-       * point primitive, render icosahedron
-       */
-      return {
-        currentVertexIndex : acc.currentVertexIndex + 12,
-        vertexString : acc.vertexString.concat(getIcoVertex(curr, scale)),
-        polyCount : acc.polyCount + 20,
-        polyString : acc.polyString.concat(getIcoPoly(acc.currentVertexIndex)),
-        labelString : acc.labelString.concat(Array(12).fill(idx.toString()).join('\n'))
-      }
-    else{
-      //curr[0] : [number,number,number][] vertices
-      //curr[1] : [number,number,number][] indices for the vertices that poly forms
-
-      /**
-       * poly primitive
-       */
-      const vertices = curr[0]
-      const polyIndices = curr[1]
-
-      return {
-        currentVertexIndex : acc.currentVertexIndex + vertices.length,
-        vertexString : acc.vertexString.concat(getMeshVertex(vertices)),
-        polyCount : acc.polyCount + polyIndices.length,
-        polyString : acc.polyString.concat(getMeshPoly(polyIndices, acc.currentVertexIndex)),
-        labelString : acc.labelString.concat(Array(vertices.length).fill(idx.toString()).join('\n'))
-      }
-    }
-  }, {
-    currentVertexIndex : 0,
-    vertexString : [],
-    polyCount : 0,
-    polyString: [],
-    labelString : [],
-  })
-
-  // if no vertices are been rendered, do not replace old
-  if(reduce.currentVertexIndex === 0)
-    return false
-
-  return vtkHeader
-    .concat('\n')
-    .concat(getVertexHeader(reduce.currentVertexIndex))
-    .concat('\n')
-    .concat(reduce.vertexString.join('\n'))
-    .concat('\n')
-    .concat(getPolyHeader(reduce.polyCount))
-    .concat('\n')
-    .concat(reduce.polyString.join('\n'))
-    .concat('\n')
-    .concat(getLabelHeader(reduce.currentVertexIndex))
-    .concat('\n')
-    .concat(reduce.labelString.join('\n'))
-}
-
 let userLandmarkVtkUrl
 
-const getuserLandmarksVtk = (action) => {
-  const landmarks = action.landmarks
-  const scale = action.scale
-    ? action.scale
-    : 2.8
-
-  /**
-   * if userlandmarks vtk is empty, that means user removed all landmarks
-   * thus, removing revoking URL, and send null as assembled userlandmark vtk
-   */
-  if (landmarks.length === 0) {
-
-    if(userLandmarkVtkUrl) URL.revokeObjectURL(userLandmarkVtkUrl)
-
-    postMessage({
-      type: 'ASSEMBLED_USERLANDMARKS_VTK'
-    })
-
-    return
-  }
-
-  const vtk = parseLmToVtk(landmarks, scale)
-  if(!vtk) return
-
-  if(userLandmarkVtkUrl)
-    URL.revokeObjectURL(userLandmarkVtkUrl)
-
-  userLandmarkVtkUrl = URL.createObjectURL(new Blob( [encoder.encode(vtk)], {type : 'application/octet-stream'} ))
-  postMessage({
-    type : 'ASSEMBLED_USERLANDMARKS_VTK',
-    url : userLandmarkVtkUrl
-  })
-}
-
 let plotyVtkUrl
 
 onmessage = (message) => {
@@ -369,16 +193,4 @@ onmessage = (message) => {
     })
     return
   }
-
-  if(validTypes.findIndex(type => type === message.data.type) >= 0){
-    switch(message.data.type){
-      case 'GET_USERLANDMARKS_VTK':
-        getuserLandmarksVtk(message.data)
-        return
-      default:
-        console.warn('unhandled worker action', message)
-    }
-  } else {
-    console.warn('unhandled worker action', message)
-  }
 }