diff --git a/src/api/service.ts b/src/api/service.ts
index 9e68398251d25590c838ae15f65af7205ffc4cd1..49b22c9ec2dc5e59648dca26fcd13e21716cd490 100644
--- a/src/api/service.ts
+++ b/src/api/service.ts
@@ -14,7 +14,7 @@ export type NAMESPACE_TYPE = "sxplr"
 export const namespace: NAMESPACE_TYPE = "sxplr"
 const nameSpaceRegex = new RegExp(`^${namespace}`)
 
-type AddableLayer = atlasAppearance.NgLayerCustomLayer
+type AddableLayer = atlasAppearance.const.NgLayerCustomLayer
 
 type AtId = {
   "@id": string
diff --git a/src/atlasComponents/sapi/sapi.service.ts b/src/atlasComponents/sapi/sapi.service.ts
index fb51ddd60f159234296d393e7f0bf65fdc209d5a..43eb63b63bcfccc3e6c453422694a7656fb3a088 100644
--- a/src/atlasComponents/sapi/sapi.service.ts
+++ b/src/atlasComponents/sapi/sapi.service.ts
@@ -210,6 +210,14 @@ export class SAPI{
       ...sapiParam
     })
   }
+
+  getV3FeatureDetailWithId(id: string) {
+    return this.v3Get("/feature/{feature_id}", {
+      path: {
+        feature_id: id
+      }
+    })
+  }
   
   getFeature(featureId: string, opts: Record<string, string> = {}) {
     return new SAPIFeature(this, featureId, opts)
@@ -366,7 +374,7 @@ export class SAPI{
           const arraybuffer = await resp.arrayBuffer()
           let outbuf: ArrayBuffer
           try {
-            outbuf = getExportNehuba().pako.inflate(arraybuffer).buffer
+            outbuf = (await getExportNehuba()).pako.inflate(arraybuffer).buffer
           } catch (e) {
             console.log("unpack error", e)
             outbuf = arraybuffer
@@ -550,7 +558,7 @@ export class SAPI{
 
     try {
       const bin = atob(content)
-      const { pako } = getExportNehuba()
+      const { pako } = await getExportNehuba()
       const array = pako.inflate(bin)
       let workerMsg: string
       switch (method) {
diff --git a/src/atlasComponents/sapi/translateV3.ts b/src/atlasComponents/sapi/translateV3.ts
index 4134af67f94b6ca59e8d72bbf4ab94ac76d527cd..63390575627568844ac47cd019858510d34a3c7d 100644
--- a/src/atlasComponents/sapi/translateV3.ts
+++ b/src/atlasComponents/sapi/translateV3.ts
@@ -100,7 +100,6 @@ class TranslateV3 {
       }
       const [transform, info] = await Promise.all([
         (async () => {
-          
           const resp = await fetch(`${precomputedVol}/transform.json`)
           if (resp.status >= 400) {
             console.error(`cannot retrieve transform: ${resp.status}`)
@@ -195,7 +194,7 @@ class TranslateV3 {
       for (const { volume: volIdx, fragment, label } of map.indices[regionname]) {
         const volume = map.volumes[volIdx || 0]
         if (!volume.formats.includes("gii-label")) {
-          console.warn(`getting three label error! volume does not provide gii-label! skipping!`)
+          // Does not support gii-label... skipping!
           continue
         }
         const { ["gii-label"]: giiLabel } = volume.providedVolumes
@@ -206,7 +205,7 @@ class TranslateV3 {
           continue
         }
         if (!giiLabel[fragment]) {
-          console.warn(`fragment '${fragment}' not provided by volume.gii-label! skipping!`)
+          // Does not support gii-label... skipping!
           continue
         }
         let laterality: 'left' | 'right'
@@ -222,7 +221,11 @@ class TranslateV3 {
     return threeLabelMap
   }
   
+  #wkmpLblMapToNgSegLayers = new WeakMap()
   async translateLabelledMapToNgSegLayers(map:PathReturn<"/map">): Promise<Record<string,{layer:NgSegLayerSpec, region: LabelledMap[]}>> {
+    if (this.#wkmpLblMapToNgSegLayers.has(map)) {
+      return this.#wkmpLblMapToNgSegLayers.get(map)
+    }
     const nglayerSpecMap: Record<string,{layer:NgSegLayerSpec, region: LabelledMap[]}> = {}
 
     const registerLayer = async (url: string, label: number, region: LabelledMap) => {
@@ -259,7 +262,8 @@ class TranslateV3 {
         const volume = map.volumes[volumeIdx]
         
         if (!volume.providedVolumes["neuroglancer/precomputed"]) {
-          console.error(`${error}, volume does not provide neuroglancer/precomputed. Skipping.`)
+          // volume does not provide neuroglancer/precomputed
+          // probably when fsaverage has been selected
           continue
         }
 
@@ -276,7 +280,7 @@ class TranslateV3 {
         await registerLayer(precomputedVol[fragment], label, { name: regionname, label })
       }
     }
-    
+    this.#wkmpLblMapToNgSegLayers.set(map, nglayerSpecMap)
     return nglayerSpecMap
   }
 
diff --git a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts
index b9212a9079ca3811050e0705fc8d2e5d3a73394b..5ad6847dcf3ea4d1156329aa63c54063dfc42a3a 100644
--- a/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts
+++ b/src/atlasComponents/sapiViews/core/rich/regionsHierarchy/regionsHierarchy.component.ts
@@ -3,7 +3,6 @@ import { UntypedFormControl } from "@angular/forms";
 import { Subscription } from "rxjs";
 import { debounceTime, distinctUntilChanged, filter, startWith } from "rxjs/operators";
 import { SxplrRegion } from "src/atlasComponents/sapi/sxplrTypes";
-import { translateV3Entities } from "src/atlasComponents/sapi/translateV3"
 import { SxplrFlatHierarchyTreeView } from "src/components/flatHierarchy/treeView/treeView.component";
 import { FilterByRegexPipe } from "./filterByRegex.pipe";
 import { RegionTreeFilterPipe } from "./regionTreeFilter.pipe";
@@ -23,13 +22,7 @@ const filterByRegexPipe = new FilterByRegexPipe()
 export class SapiViewsCoreRichRegionsHierarchy {
 
   static IsParent(region: SxplrRegion, parentRegion: SxplrRegion): boolean {
-    const _region = translateV3Entities.retrieveRegion(region)
-    const _parentRegion = translateV3Entities.retrieveRegion(parentRegion)
-    const { ["@id"]: parentRegionId } = _parentRegion
-    return _region.hasParent?.some(parent => {
-      const { ["@id"]: pId } = parent
-      return pId === parentRegionId
-    })
+    return region.parentIds.some(id => parentRegion.id === id)
   }
 
   static FilterRegions(regions: SxplrRegion[], searchTerm: string): SxplrRegion[]{
diff --git a/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.scss b/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.scss
index e39c0ecb5869b91204fd4782600e6686f8581cee..733e12f491703e2a25badbdc40f9b3cf89526482 100644
--- a/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.scss
+++ b/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.scss
@@ -9,3 +9,9 @@
 {
     width: 100%;
 }
+
+spinner-cmp
+{
+    margin: auto;
+    margin-left: 0.5rem;
+}
diff --git a/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.ts b/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.ts
index 903ec3ffef6a55045266ce0efea3c610fd5d1630..59dda8f6107c4e2dadf173fc0bbf43b95857a72a 100644
--- a/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.ts
+++ b/src/atlasComponents/sapiViews/features/feature-view/feature-view.component.ts
@@ -30,7 +30,6 @@ export class FeatureViewComponent implements OnChanges {
   constructor(private sapi: SAPI) { }
 
   ngOnChanges(): void {
-    console.log(this.feature)
     this.tabular$.next(null)
     this.busy$.next(true)
     this.sapi.v3Get("/feature/{feature_id}", {
diff --git a/src/atlasComponents/userAnnotations/tools/service.ts b/src/atlasComponents/userAnnotations/tools/service.ts
index 5b35420884dd340e0e524bab253e7876aa1cb712..166efee58d09603f68f710bf2576b11c13943c5e 100644
--- a/src/atlasComponents/userAnnotations/tools/service.ts
+++ b/src/atlasComponents/userAnnotations/tools/service.ts
@@ -12,7 +12,6 @@ import { Polygon } from "./poly";
 import { Line } from "./line";
 import { Point } from "./point";
 import { FilterAnnotationsBySpace } from "../filterAnnotationBySpace.pipe";
-import { retry } from 'common/util'
 import { MatSnackBar } from "@angular/material/snack-bar";
 import { actions } from "src/state/atlasSelection";
 import { atlasSelection } from "src/state";
@@ -535,15 +534,7 @@ export class ModularUserAnnotationToolService implements OnDestroy{
     if (!encoded) return []
     const bin = atob(encoded)
     
-    await retry(() => {
-      if (!!getExportNehuba()) return true
-      else throw new Error(`export nehuba not yet ready`)
-    }, {
-      timeout: 1000,
-      retries: 10
-    })
-    
-    const { pako } = getExportNehuba()
+    const { pako } = await getExportNehuba()
     const decoded = pako.inflate(bin, { to: 'string' })
     const arr = JSON.parse(decoded)
     const anns: IAnnotationGeometry[] = []
@@ -572,14 +563,14 @@ export class ModularUserAnnotationToolService implements OnDestroy{
    */
   private metadataMap = new Map<string, TAnnotationMetadata>()
 
-  private storeAnnotation(anns: IAnnotationGeometry[]){
+  private async storeAnnotation(anns: IAnnotationGeometry[]){
     const arr = []
     for (const ann of anns) {
       const json = ann.toJSON()
       arr.push(json)
     }
     const stringifiedJSON = JSON.stringify(arr)
-    const exportNehuba = getExportNehuba()
+    const exportNehuba = await getExportNehuba()
     if (!exportNehuba) return
     const { pako } = exportNehuba
     const compressed = pako.deflate(stringifiedJSON)
diff --git a/src/features/entry/entry.component.html b/src/features/entry/entry.component.html
index 97802ff5f431881e22e9e20ba78169cd48db088f..c7fb2d5dc679952e9d9cf7714e24d7119539b7dd 100644
--- a/src/features/entry/entry.component.html
+++ b/src/features/entry/entry.component.html
@@ -15,9 +15,11 @@
                 
                 <mat-panel-description>
                     <spinner-cmp *ngIf="categoryAcc.isBusy$ | async"></spinner-cmp>
-                    <span>
-                        {{ categoryAcc.total$ | async }}
-                    </span>
+                    <ng-template [ngIf]="categoryAcc.total$ | async" let-total>
+                        <span>
+                            {{ total }}
+                        </span>
+                    </ng-template>
                 </mat-panel-description>
             </mat-expansion-panel-header>
 
@@ -36,6 +38,7 @@
                             </mat-card-title>
                         </mat-card-header>
                         <mat-card-content>
+                            <spinner-cmp *ngIf="(list.state$ | async) === 'busy'"></spinner-cmp>
                             <sxplr-feature-list
                                 [template]="template"
                                 [parcellation]="parcellation"
@@ -45,8 +48,7 @@
                                 (onClickFeature)="onClickFeature($event)"
                                 #list="featureList"
                                 >
-                            </sxplr-feature-list>
-                            <spinner-cmp *ngIf="(list.state$ | async) === 'busy'"></spinner-cmp>
+                            </sxplr-feature-list>                            
                         </mat-card-content>
                     </mat-card>
                 </div>
diff --git a/src/features/list/list.component.html b/src/features/list/list.component.html
index c50515f2580ecaa0d415a5e5e9b14fb7639aae3b..28d0b7dabbee811aecda8eda61e2ec33abf58866 100644
--- a/src/features/list/list.component.html
+++ b/src/features/list/list.component.html
@@ -1,11 +1,11 @@
-<mat-list>
-    <mat-list-item mat-ripple
-        *ngFor="let feature of features$ | async"
+<cdk-virtual-scroll-viewport itemSize="36"
+    class="virtual-scroll-viewport">
+    <button *cdkVirtualFor="let feature of features$ | async"
+        mat-button
+        class="virtual-scroll-item sxplr-w-100"
         [matTooltip]="feature.name"
         matTooltipPosition="right"
         (click)="onClickItem(feature)">
-        <span class="feature-name">
-            {{ feature.name }}
-        </span>
-    </mat-list-item>
-</mat-list>
+        {{ feature.name }}
+    </button>
+</cdk-virtual-scroll-viewport>
diff --git a/src/features/list/list.component.scss b/src/features/list/list.component.scss
index 99448693600d945c9af3622013e7f196f900d221..97873a44ba16b4cd541b11a786a29cf1fc979125 100644
--- a/src/features/list/list.component.scss
+++ b/src/features/list/list.component.scss
@@ -2,6 +2,7 @@
 {
     display: block;
     width: 100%;
+    height:100%;
 }
 
 .feature-name
@@ -13,3 +14,13 @@
 {
     cursor: default;
 }
+
+.virtual-scroll-viewport
+{
+    height: 100%;
+}
+
+.virtual-scroll-item
+{
+    height:36px;
+}
diff --git a/src/features/module.ts b/src/features/module.ts
index 7de3d6ea36a0404f869ca7a8a0b8663815b1e750..958956bea943a9f27ebc2c8c0fc07d76661ce30a 100644
--- a/src/features/module.ts
+++ b/src/features/module.ts
@@ -13,6 +13,8 @@ import { FetchDirective } from "./fetch.directive";
 import { ListComponent } from './list/list.component';
 import { CategoryAccDirective } from './category-acc.directive';
 import { SapiViewsFeatureConnectivityModule } from "./connectivity";
+import { ScrollingModule } from "@angular/cdk/scrolling";
+import { MatButtonModule } from "@angular/material/button"
 
 @NgModule({
   imports: [
@@ -24,7 +26,9 @@ import { SapiViewsFeatureConnectivityModule } from "./connectivity";
     MatTooltipModule,
     UtilModule,
     MatRippleModule,
-    SapiViewsFeatureConnectivityModule
+    SapiViewsFeatureConnectivityModule,
+    ScrollingModule,
+    MatButtonModule,
   ],
   declarations: [
     EntryComponent,
diff --git a/src/main.module.ts b/src/main.module.ts
index b681764f400aa4f8fabea3a4544a3ca03b64dc9a..a0ddf6d025324c91da5836c9b9412c5f9541e04f 100644
--- a/src/main.module.ts
+++ b/src/main.module.ts
@@ -50,6 +50,7 @@ import { EffectsModule } from '@ngrx/effects';
 import { LayerCtrlEffects } from './viewerModule/nehuba/layerCtrl.service/layerCtrl.effects';
 import { NehubaNavigationEffects } from './viewerModule/nehuba/navigation.service/navigation.effects';
 import { CONST } from "common/constants"
+import { ViewerCommonEffects } from './viewerModule';
 
 @NgModule({
   imports: [
@@ -77,6 +78,7 @@ import { CONST } from "common/constants"
       ...getStoreEffects(),
       LayerCtrlEffects,
       NehubaNavigationEffects,
+      ViewerCommonEffects,
     ]),
     RootStoreModule,
     HttpClientModule,
diff --git a/src/messaging/nmvSwc/index.ts b/src/messaging/nmvSwc/index.ts
index 2f4e7586970acd9450a6456fb9fe6e06ba7ddea9..6dc9abb482da80c3ffaf76c2478211587de31255 100644
--- a/src/messaging/nmvSwc/index.ts
+++ b/src/messaging/nmvSwc/index.ts
@@ -5,15 +5,6 @@ import { INmvTransform } from "./type"
 
 export const TYPE = 'bas:datasource'
 
-const waitFor = (condition: (...arg: any[]) => boolean) => new Promise<void>((rs, rj) => {
-  const intervalRef = setInterval(() => {
-    if (condition()) {
-      clearInterval(intervalRef)
-      rs()
-    }
-  }, 1000)
-})
-
 const NM_IDS = {
   AMBA_V3: 'hbp:Allen_Mouse_CCF_v3(um)',
   WAXHOLM_V1_01: 'hbp:WHS_SD_Rat_v1.01(um)',
@@ -110,16 +101,15 @@ export const processJsonLd = (json: { [key: string]: any }): Observable<IMessagi
       }
     })
 
-    await waitFor(() => !!getExportNehuba())
-
     const b64Encoded = encoding.indexOf('base64') >= 0
     const isGzipped = encoding.indexOf('gzip') >= 0
     let data = rawData
     if (b64Encoded) {
       data = atob(data)
     }
+    const { pako, mat3, vec3 } = await getExportNehuba()
     if (isGzipped) {
-      data = getExportNehuba().pako.inflate(data)
+      data = pako.inflate(data)
     }
     let output = ``
     for (let i = 0; i < data.length; i++) {
@@ -146,7 +136,6 @@ export const processJsonLd = (json: { [key: string]: any }): Observable<IMessagi
     ]
     // NG translation works on nm scale
     const scaleUmToNm = 1e3
-    const { mat3, vec3 } = getExportNehuba()
     const modA = mat3.fromValues(
       scaleUmToVoxelFixed[0], 0, 0,
       0, scaleUmToVoxelFixed[1], 0,
diff --git a/src/overwrite.scss b/src/overwrite.scss
index a60e2755623bed99f769435701c0798d47ce927d..42cdf515c7483f04addad9c19d02ded67e9aaf6e 100644
--- a/src/overwrite.scss
+++ b/src/overwrite.scss
@@ -289,3 +289,8 @@ a[mat-raised-button]
 {
   pointer-events: none!important;
 }
+
+.virtual-scroll-viewport > .cdk-virtual-scroll-content-wrapper
+{
+  width: 100%;
+}
diff --git a/src/routerModule/routeStateTransform.service.ts b/src/routerModule/routeStateTransform.service.ts
index 8a6fe4d335e622fc62672716790827f3cb9ba2ba..86cad2fd1291c9aaff05285005e1319c605ef311 100644
--- a/src/routerModule/routeStateTransform.service.ts
+++ b/src/routerModule/routeStateTransform.service.ts
@@ -228,7 +228,7 @@ export class RouteStateTransformSvc {
     try {
       if (returnObj.f && returnObj.f.length === 1) {
         const decodedFeatId = decodeId(returnObj.f[0])
-        const feature = await this.sapi.getFeature(decodedFeatId).detail$.toPromise()
+        const feature = await this.sapi.getV3FeatureDetailWithId(decodedFeatId).toPromise()
         returnState["[state.userInteraction]"].selectedFeature = feature
       }
     } catch (e) {
diff --git a/src/state/atlasAppearance/action.ts b/src/state/atlasAppearance/action.ts
index 6a4ce26e148d801c726ba23c3eee906e736aa510..9cdf1fcf4888766f314fd1a411435741d473c1f6 100644
--- a/src/state/atlasAppearance/action.ts
+++ b/src/state/atlasAppearance/action.ts
@@ -1,5 +1,5 @@
 import { createAction, props } from "@ngrx/store";
-import { CustomLayer, nameSpace } from "./const"
+import { CustomLayer, nameSpace, UseViewer } from "./const"
 
 export const setOctantRemoval = createAction(
   `${nameSpace} setOctantRemoval`,
@@ -28,3 +28,10 @@ export const removeCustomLayer = createAction(
     id: string
   }>()
 )
+
+export const setUseViewer = createAction(
+  `${nameSpace} useViewer`,
+  props<{
+    viewer: UseViewer
+  }>()
+)
diff --git a/src/state/atlasAppearance/const.ts b/src/state/atlasAppearance/const.ts
index 910acab7a8607b74e5b1a4c5d9d06491145fabe2..41c9ebd7216465719aab792025b8ad2b5b268f6d 100644
--- a/src/state/atlasAppearance/const.ts
+++ b/src/state/atlasAppearance/const.ts
@@ -50,3 +50,11 @@ export type NgLayerCustomLayer = {
  * - id allows custom layer to be removed, if necessary
  */
 export type CustomLayer = ColorMapCustomLayer | NgLayerCustomLayer | ThreeSurferCustomLayer | ThreeSurferCustomLabelLayer
+
+export const useViewer = {
+  THREESURFER: "THREESURFER",
+  NEHUBA: "NEHUBA",
+  NOT_SUPPORTED: "NOT_SUPPORTED" 
+} as const
+
+export type UseViewer = keyof typeof useViewer
diff --git a/src/state/atlasAppearance/index.ts b/src/state/atlasAppearance/index.ts
index 739f036f65baf71930eff7ae9fd37067cfb4e012..5a133c488c65470459968c71d918e1bce322a0a0 100644
--- a/src/state/atlasAppearance/index.ts
+++ b/src/state/atlasAppearance/index.ts
@@ -1,4 +1,5 @@
 export * as actions from "./action"
 export * as selectors from "./selector"
-export { nameSpace, ColorMapCustomLayer, CustomLayer, NgLayerCustomLayer } from "./const"
+export * as const from "./const"
+export { nameSpace } from "./const"
 export { reducer, AtlasAppearanceStore, defaultState } from "./store"
diff --git a/src/state/atlasAppearance/selector.ts b/src/state/atlasAppearance/selector.ts
index b7eac7bc1701c6e490a6a72ccd9cf8412d4e7aeb..05b5aface33998d9477e0cc88d851fffe24c716a 100644
--- a/src/state/atlasAppearance/selector.ts
+++ b/src/state/atlasAppearance/selector.ts
@@ -18,3 +18,8 @@ export const customLayers = createSelector(
   selectStore,
   state => state.customLayers
 )
+
+export const useViewer = createSelector(
+  selectStore,
+  state => state.useViewer
+)
diff --git a/src/state/atlasAppearance/store.ts b/src/state/atlasAppearance/store.ts
index 6c060da26b4696af0865d0f5c545fdc0d42cffd4..384655c0e3ba44d774926182ad7cc09eb05719ec 100644
--- a/src/state/atlasAppearance/store.ts
+++ b/src/state/atlasAppearance/store.ts
@@ -1,15 +1,16 @@
 import { createReducer, on } from "@ngrx/store"
 import * as actions from "./action"
-import { CustomLayer } from "./const"
+import { UseViewer, CustomLayer } from "./const"
 
 export type AtlasAppearanceStore = {
-
+  useViewer: UseViewer
   octantRemoval: boolean
   showDelineation: boolean
   customLayers: CustomLayer[]
 }
 
 export const defaultState: AtlasAppearanceStore = {
+  useViewer: null,
   octantRemoval: true,
   showDelineation: true,
   customLayers: []
@@ -58,5 +59,14 @@ export const reducer = createReducer(
         customLayers: customLayers.filter(l => l.id !== id)
       }
     }
+  ),
+  on(
+    actions.setUseViewer,
+    (state, { viewer }) => {
+      return {
+        ...state,
+        useViewer: viewer
+      }
+    }
   )
 )
diff --git a/src/util/fn.ts b/src/util/fn.ts
index 14e561f032012ea44cf5278e963cf84979cabe2c..271c783f68237e7cce54b4b6e5ebbcd901fe9372 100644
--- a/src/util/fn.ts
+++ b/src/util/fn.ts
@@ -17,8 +17,13 @@ export function getDebug() {
   return (window as any).__DEBUG__
 }
 
-export function getExportNehuba() {
-  return (window as any).export_nehuba
+export async function getExportNehuba() {
+  while (true) {
+
+    const nehuba = (window as any).export_nehuba
+    if (!!nehuba) return nehuba
+    await new Promise((rs) => setTimeout(rs, 160))
+  }
 }
 
 const recursiveFlatten = (region, {ngId}) => {
diff --git a/src/viewerModule/index.ts b/src/viewerModule/index.ts
index 6e5c74fbe55be040c17b54d98c7b6f29cd66a513..adcd5938e479a67360e364f7740984d0c40384cd 100644
--- a/src/viewerModule/index.ts
+++ b/src/viewerModule/index.ts
@@ -1 +1,2 @@
-export { ViewerModule } from "./module"
\ No newline at end of file
+export { ViewerModule } from "./module"
+export { ViewerCommonEffects } from "./viewer.common.effects"
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts
index a12d8fcfd2ad87cb8c2e545f7c43e657b4fcd70c..0253e4eef876e510be04ab6effb78244c2d1adc1 100644
--- a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.effects.ts
@@ -1,27 +1,25 @@
 import { Injectable } from "@angular/core";
 import { createEffect } from "@ngrx/effects";
 import { select, Store } from "@ngrx/store";
-import { forkJoin, from, NEVER, of, throwError } from "rxjs";
-import { mapTo, switchMap, withLatestFrom, filter, catchError, map, debounceTime, shareReplay, distinctUntilChanged, startWith, pairwise, tap } from "rxjs/operators";
-import { NgSegLayerSpec, SxplrAtlas, SxplrParcellation, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
+import { forkJoin, from, of } from "rxjs";
+import { switchMap, withLatestFrom, filter, catchError, map, debounceTime, shareReplay, distinctUntilChanged, startWith, pairwise, tap } from "rxjs/operators";
+import { Feature, NgSegLayerSpec, SxplrAtlas, SxplrParcellation, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
 import { SAPI } from "src/atlasComponents/sapi"
 import { 
   SapiFeatureModel,
   SapiSpatialFeatureModel,
 } from "src/atlasComponents/sapi/typeV3";
-import { translateV3Entities } from "src/atlasComponents/sapi/translateV3"
 import { atlasAppearance, atlasSelection, userInteraction } from "src/state";
 import { arrayEqual } from "src/util/array";
 import { EnumColorMapName } from "src/util/colorMaps";
 import { getShader } from "src/util/constants";
 import { PMAP_LAYER_NAME } from "../constants";
 import { QuickHash } from "src/util/fn";
-import { BaseService } from "../base.service/base.service";
 import { getParcNgId } from "../config.service";
 
 @Injectable()
 export class LayerCtrlEffects {
-  static TransformVolumeModel(volumeModel: SapiSpatialFeatureModel['volume']): atlasAppearance.NgLayerCustomLayer[] {
+  static TransformVolumeModel(volumeModel: SapiSpatialFeatureModel['volume']): atlasAppearance.const.NgLayerCustomLayer[] {
     /**
      * TODO implement
      */
@@ -33,67 +31,76 @@ export class LayerCtrlEffects {
     return []
   }
 
-  onRegionSelectClearPmapLayer = createEffect(() => this.store.pipe(
-    select(atlasSelection.selectors.selectedRegions),
-    distinctUntilChanged(
-      arrayEqual((o, n) => o.name === n.name)
-    ),
-    mapTo(
-      atlasAppearance.actions.removeCustomLayer({
-        id: PMAP_LAYER_NAME
-      })
-    )
-  ))
+  #onATP$ = this.store.pipe(
+    atlasSelection.fromRootStore.distinctATP(),
+    map(val => val as { atlas: SxplrAtlas, parcellation: SxplrParcellation, template: SxplrTemplate }),
+  )
+
 
   #pmapUrl: string
-  onRegionSelectShowNewPmapLayer = createEffect(() => this.store.pipe(
-    select(atlasSelection.selectors.selectedRegions),
-    filter(regions => regions.length === 1),
-    map(regions => regions[0]),
-    distinctUntilChanged((ro, rn) => ro.name === rn.name),
-    withLatestFrom(
-      this.store.pipe(
-        atlasSelection.fromRootStore.distinctATP()
-      )
-    ),
-    switchMap(([ region, { parcellation, template } ]) => {
-      return this.sapi.getStatisticalMap(parcellation, template, region).pipe(
-        catchError(() => NEVER),
-        map(({ buffer, meta }) => {
-          if (!!this.#pmapUrl) {
-            URL.revokeObjectURL(this.#pmapUrl)
+  #cleanupUrl(){
+    if (!!this.#pmapUrl) {
+      URL.revokeObjectURL(this.#pmapUrl)
+      this.#pmapUrl = null
+    }
+  }
+
+  onRegionSelect = createEffect(() => this.store.pipe(
+    select(atlasAppearance.selectors.useViewer),
+    switchMap(viewer => {
+      const rmPmapAction = atlasAppearance.actions.removeCustomLayer({
+        id: PMAP_LAYER_NAME
+      })
+      if (viewer !== "NEHUBA") {
+        this.#cleanupUrl()
+        return of(rmPmapAction)
+      }
+      return this.store.pipe(
+        select(atlasSelection.selectors.selectedRegions),
+        distinctUntilChanged(
+          arrayEqual((o, n) => o.name === n.name)
+        ),
+        withLatestFrom(this.#onATP$),
+        // since region selection changed, pmap will definitely be removed. revoke the url resource.
+        tap(() => this.#cleanupUrl()),
+        switchMap(([ regions, { parcellation, template } ]) => {
+          if (regions.length !== 1) {
+            return of(rmPmapAction)
           }
-          this.#pmapUrl = URL.createObjectURL(new Blob([buffer], {type: "application/octet-stream"}))
-          return atlasAppearance.actions.addCustomLayer({
-            customLayer: {
-              clType: "customlayer/nglayer",
-              id: PMAP_LAYER_NAME,
-              source: `nifti://${this.#pmapUrl}`,
-              shader: getShader({
-                colormap: EnumColorMapName.VIRIDIS,
-                highThreshold: meta.max,
-                lowThreshold: meta.min,
-                removeBg: true,
-              })
-            }
-          })
+          return this.sapi.getStatisticalMap(parcellation, template, regions[0]).pipe(
+            switchMap(({ buffer, meta }) => {
+              this.#pmapUrl = URL.createObjectURL(new Blob([buffer], {type: "application/octet-stream"}))
+              return of(
+                rmPmapAction,
+                atlasAppearance.actions.addCustomLayer({
+                  customLayer: {
+                    clType: "customlayer/nglayer",
+                    id: PMAP_LAYER_NAME,
+                    source: `nifti://${this.#pmapUrl}`,
+                    shader: getShader({
+                      colormap: EnumColorMapName.VIRIDIS,
+                      highThreshold: meta.max,
+                      lowThreshold: meta.min,
+                      removeBg: true,
+                    })
+                  }
+                })
+              )
+            }),
+            catchError(() => of(rmPmapAction)),
+          )
         })
       )
-    }),
+    })
   ))
 
-  onATP$ = this.store.pipe(
-    atlasSelection.fromRootStore.distinctATP(),
-    map(val => val as { atlas: SxplrAtlas, parcellation: SxplrParcellation, template: SxplrTemplate }),
-  )
-
   onShownFeature = createEffect(() => this.store.pipe(
     select(userInteraction.selectors.selectedFeature),
-    startWith(null as SapiFeatureModel),
-    pairwise<SapiFeatureModel>(),
+    startWith(null as Feature),
+    pairwise(),
     map(([ prev, curr ]) => {
-      const removeLayers: atlasAppearance.NgLayerCustomLayer[] = []
-      const addLayers: atlasAppearance.NgLayerCustomLayer[] = []
+      const removeLayers: atlasAppearance.const.NgLayerCustomLayer[] = []
+      const addLayers: atlasAppearance.const.NgLayerCustomLayer[] = []
       if (prev?.["@type"].includes("feature/volume_of_interest")) {
         const prevVoi = prev as SapiSpatialFeatureModel
         removeLayers.push(
@@ -119,7 +126,7 @@ export class LayerCtrlEffects {
     ]))
   ))
 
-  onATPClearBaseLayers = createEffect(() => this.onATP$.pipe(
+  onATPClearBaseLayers = createEffect(() => this.#onATP$.pipe(
     withLatestFrom(
       this.store.pipe(
         select(atlasAppearance.selectors.customLayers),
@@ -142,7 +149,7 @@ export class LayerCtrlEffects {
     )
   ))
 
-  onATPDebounceNgLayers$ = this.onATP$.pipe(
+  onATPDebounceNgLayers$ = this.#onATP$.pipe(
     debounceTime(16),
     switchMap(({ atlas, template, parcellation }) => 
       forkJoin({
@@ -190,7 +197,7 @@ export class LayerCtrlEffects {
     switchMap(ngLayers => {
       const { parcNgLayers, tmplAuxNgLayers, tmplNgLayers } = ngLayers
       
-      const customBaseLayers: atlasAppearance.NgLayerCustomLayer[] = []
+      const customBaseLayers: atlasAppearance.const.NgLayerCustomLayer[] = []
       for (const layers of [parcNgLayers, tmplAuxNgLayers, tmplNgLayers]) {
         for (const key in layers) {
           const { source, transform, opacity, visible } = layers[key]
@@ -217,6 +224,5 @@ export class LayerCtrlEffects {
   constructor(
     private store: Store<any>,
     private sapi: SAPI,
-    private baseService: BaseService,
   ){}
 }
\ No newline at end of file
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
index 3035dc2620c7d1c437e49022f14e12e94fae356e..b1c82b156dfa5888c6b828b944641c66bde8d7b6 100644
--- a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts
@@ -1,19 +1,16 @@
 import { Injectable, OnDestroy } from "@angular/core";
 import { select, Store } from "@ngrx/store";
-import { combineLatest, from, merge, Observable, Subject, Subscription } from "rxjs";
+import { combineLatest, merge, Observable, Subject, Subscription } from "rxjs";
 import { debounceTime, distinctUntilChanged, filter, map, pairwise, shareReplay, startWith, switchMap, withLatestFrom } from "rxjs/operators";
 import { IColorMap, INgLayerCtrl, TNgLayerCtrl } from "./layerCtrl.util";
-import { getParcNgId } from "../config.service"
 import { annotation, atlasAppearance, atlasSelection } from "src/state";
 import { serializeSegment } from "../util";
 import { LayerCtrlEffects } from "./layerCtrl.effects";
 import { arrayEqual } from "src/util/array";
-import { ColorMapCustomLayer } from "src/state/atlasAppearance";
 import { SxplrRegion } from "src/atlasComponents/sapi/sxplrTypes";
 import { AnnotationLayer } from "src/atlasComponents/annotations";
 import { PMAP_LAYER_NAME } from "../constants"
 import { getShader } from "src/util/constants";
-import { SAPI } from "src/atlasComponents/sapi";
 import { BaseService } from "../base.service/base.service";
 
 export const BACKUP_COLOR = {
@@ -55,8 +52,8 @@ export class NehubaLayerControlService implements OnDestroy{
       map(([record, layers]) => {
         const returnVal: IColorMap = {}
 
-        const cmCustomLayers = layers.filter(l => l.clType === "customlayer/colormap") as ColorMapCustomLayer[]
-        const cmBaseLayers = layers.filter(l => l.clType === "baselayer/colormap") as ColorMapCustomLayer[]
+        const cmCustomLayers = layers.filter(l => l.clType === "customlayer/colormap") as atlasAppearance.const.ColorMapCustomLayer[]
+        const cmBaseLayers = layers.filter(l => l.clType === "baselayer/colormap") as atlasAppearance.const.ColorMapCustomLayer[]
         
         const useCm = (() => {
           /**
@@ -81,7 +78,7 @@ export class NehubaLayerControlService implements OnDestroy{
         for (const [ngId, labelRecord] of Object.entries(record)) {
           for (const [label, region] of Object.entries(labelRecord)) {
             if (!region.color) continue
-            const [ red, green, blue ] = useCm.get(region)
+            const [ red, green, blue ] = useCm.get(region) || [200, 200, 200]
             if (!returnVal[ngId]) {
               returnVal[ngId] = {}
             }
@@ -210,13 +207,6 @@ export class NehubaLayerControlService implements OnDestroy{
   /**
    * define when shown segments should be updated
    */
-  public _segmentVis$: Observable<string[]> = combineLatest([
-    this.selectedATP$,
-    this.selectedRegion$
-  ]).pipe(
-    map(() => [''])
-  )
-
   public segmentVis$: Observable<string[]> = combineLatest([
     /**
      * selectedRegions
@@ -232,9 +222,9 @@ export class NehubaLayerControlService implements OnDestroy{
       map(layers => layers.filter(l => l.clType === "customlayer/nglayer").length > 0),
     ),
   ]).pipe(
-    withLatestFrom(this.completeNgIdLabelRegionMap$),
-    map(([[ selectedRegions, customMapExists, nonmixableLayerExists ], completeNgIdLabelRegion]) => {
-      /**
+    switchMap(( [ selectedRegions, customMapExists, nonmixableLayerExists ] ) => this.completeNgIdLabelRegionMap$.pipe(
+      map(completeNgIdLabelRegion => {
+        /**
        * if non mixable layer exist (e.g. pmap)
        * and no custom color map exist
        * hide all segmentations
@@ -263,19 +253,20 @@ export class NehubaLayerControlService implements OnDestroy{
       } else {
         return []
       }
-    })
+      }),
+    )),
   )
 
   /**
    * ngLayers controller
    */
 
-  private ngLayersRegister: atlasAppearance.NgLayerCustomLayer[] = []
+  private ngLayersRegister: atlasAppearance.const.NgLayerCustomLayer[] = []
 
-  private getUpdatedCustomLayer(isSameLayer: (o: atlasAppearance.NgLayerCustomLayer, n: atlasAppearance.NgLayerCustomLayer) => boolean){
+  private getUpdatedCustomLayer(isSameLayer: (o: atlasAppearance.const.NgLayerCustomLayer, n: atlasAppearance.const.NgLayerCustomLayer) => boolean){
     return this.store$.pipe(
       select(atlasAppearance.selectors.customLayers),
-      map(customLayers => customLayers.filter(l => l.clType === "customlayer/nglayer") as atlasAppearance.NgLayerCustomLayer[]),
+      map(customLayers => customLayers.filter(l => l.clType === "customlayer/nglayer") as atlasAppearance.const.NgLayerCustomLayer[]),
       pairwise(),
       map(([ oldCustomLayers, newCustomLayers ]) => {
         return newCustomLayers.filter(n => oldCustomLayers.some(o => o.id === n.id && !isSameLayer(o, n)))
@@ -288,7 +279,7 @@ export class NehubaLayerControlService implements OnDestroy{
   private updateCustomLayerColorMap$ = this.getUpdatedCustomLayer((o, n) => o.shader === n.shader)
 
   private ngLayers$ = this.customLayers$.pipe(
-    map(customLayers => customLayers.filter(l => l.clType === "customlayer/nglayer") as atlasAppearance.NgLayerCustomLayer[]),
+    map(customLayers => customLayers.filter(l => l.clType === "customlayer/nglayer") as atlasAppearance.const.NgLayerCustomLayer[]),
     distinctUntilChanged(
       arrayEqual((o, n) => o.id === n.id)
     ),
@@ -374,7 +365,7 @@ export class NehubaLayerControlService implements OnDestroy{
     ),
     this.ngLayers$.pipe(
       map(({ customLayers }) => customLayers),
-      startWith([] as atlasAppearance.NgLayerCustomLayer[]),
+      startWith([] as atlasAppearance.const.NgLayerCustomLayer[]),
       map(customLayers => {
         /**
          * pmap control has its own visibility controller
diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.util.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.util.ts
index 1fa9f52ff34373fdb76dd62ddeb7acffc71ff091..6c7c7cd24d389a426f6b17ef134a495dedf11061 100644
--- a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.util.ts
+++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.util.ts
@@ -43,10 +43,10 @@ export interface INgLayerCtrl {
     names: string[]
   }
   add: {
-    [key: string]: atlasAppearance.NgLayerCustomLayer
+    [key: string]: atlasAppearance.const.NgLayerCustomLayer
   }
   update: {
-    [key: string]: Partial<atlasAppearance.NgLayerCustomLayer>
+    [key: string]: Partial<atlasAppearance.const.NgLayerCustomLayer>
   }
   setLayerTransparency: {
     [key: string]: number
diff --git a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
index 10d7ba68683f16c5a09b0df46a954380480fe645..53c0afb97bb0933a327c68ff675d773cc4f008c2 100644
--- a/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewer/nehubaViewer.component.ts
@@ -147,10 +147,12 @@ export class NehubaViewerUnit implements OnDestroy {
     if (this.nehubaViewer$) {
       this.nehubaViewer$.next(this)
     }
+
     getImportNehubaPr()
-      .then(() => {
+      .then(() => getExportNehuba())
+      .then(exportNehuba => {
         this.nehubaLoaded = true
-        this.exportNehuba = getExportNehuba()
+        this.exportNehuba = exportNehuba
         const fixedZoomPerspectiveSlices = this.config && this.config.layout && this.config.layout.useNehubaPerspective && this.config.layout.useNehubaPerspective.fixedZoomPerspectiveSlices
         if (fixedZoomPerspectiveSlices) {
           const { sliceZoom, sliceViewportWidth, sliceViewportHeight } = fixedZoomPerspectiveSlices
diff --git a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerContainer.component.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerContainer.component.ts
index f3c063b51ffb646e2eb9a9be701b3670ee00585d..2c0184e79b7304b440473addb8e3e0f71c9814f5 100644
--- a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerContainer.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerContainer.component.ts
@@ -6,7 +6,6 @@ import { NehubaLayerControlService } from "../layerCtrl.service";
 import { NehubaViewerContainerDirective } from "./nehubaViewerInterface.directive";
 import { Store } from "@ngrx/store";
 import { atlasSelection } from "src/state";
-import { SAPI } from "src/atlasComponents/sapi";
 
 @Component({
   selector: `sxplr-nehuba-viewer-container`,
diff --git a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
index 36738660446e4cb2017bd630889c376d286de6c8..6454ea378c353eab8487b686f2009759eb95e819 100644
--- a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
+++ b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerInterface.directive.ts
@@ -10,7 +10,6 @@ import { INavObj, NehubaNavigationService } from "../navigation.service";
 import { NehubaConfig, defaultNehubaConfig, getNehubaConfig } from "../config.service";
 import { atlasAppearance, atlasSelection, userPreference } from "src/state";
 import { SxplrAtlas, SxplrParcellation, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
-import { NgLayerCustomLayer } from "src/state/atlasAppearance";
 import { arrayEqual } from "src/util/array";
 import { cvtNavigationObjToNehubaConfig } from "../config.service/util";
 import { LayerCtrlEffects } from "../layerCtrl.service/layerCtrl.effects";
@@ -178,7 +177,7 @@ export class NehubaViewerContainerDirective implements OnDestroy{
         switchMap((ATP: { atlas: SxplrAtlas, parcellation: SxplrParcellation, template: SxplrTemplate }) => this.store$.pipe(
           select(atlasAppearance.selectors.customLayers),
           debounceTime(16),
-          map(cl => cl.filter(l => l.clType === "baselayer/nglayer") as NgLayerCustomLayer[]),
+          map(cl => cl.filter(l => l.clType === "baselayer/nglayer") as atlasAppearance.const.NgLayerCustomLayer[]),
           distinctUntilChanged(arrayEqual((oi, ni) => oi.id === ni.id)),
           filter(layers => layers.length > 0),
           map(ngBaseLayers => {
diff --git a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerTouch.directive.ts b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerTouch.directive.ts
index 0fbd2562a1d4f50bc39e6157aefe322747afe475..6223e187fe07f0bd19f2f5774c793594ff17851f 100644
--- a/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerTouch.directive.ts
+++ b/src/viewerModule/nehuba/nehubaViewerInterface/nehubaViewerTouch.directive.ts
@@ -43,13 +43,7 @@ export class NehubaViewerTouchDirective implements OnDestroy{
     return this.htmlElementIndexMap.get(panel)
   }
 
-  private _exportNehuba: any
-  private get exportNehuba(){
-    if (!this._exportNehuba) {
-      this._exportNehuba = getExportNehuba()
-    }
-    return this._exportNehuba
-  }
+  private exportNehuba: any
 
   private s: Subscription[] = []
   private nehubaSub: Subscription[] = []
@@ -67,6 +61,7 @@ export class NehubaViewerTouchDirective implements OnDestroy{
         })
       )
     }
+    getExportNehuba().then(exportNehuba => this.exportNehuba = exportNehuba)
     /**
      * Touchend also needs to be listened to, as user could start
      * with multitouch, and end up as single touch
@@ -262,8 +257,9 @@ export class NehubaViewerTouchDirective implements OnDestroy{
         if (isNaN(deltaX) || isNaN(deltaX)) return
         const { position } = this.ngViewer.navigationState
         const pos = position.spatialCoordinates
-        this.exportNehuba.vec3.set(pos, deltaX, deltaY, 0)
-        this.exportNehuba.vec3.transformMat4(pos, pos, this.viewportToData[panelIndex])
+        const { vec3 } = this.exportNehuba
+        vec3.set(pos, deltaX, deltaY, 0)
+        vec3.transformMat4(pos, pos, this.viewportToData[panelIndex])
 
         position.changed.dispatch()
       })
diff --git a/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.component.ts b/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.component.ts
index ea321e012cccbbe69e27f0e8467b14b945876e00..cce89a171435f72d6c17f112548f92477ad47dc1 100644
--- a/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.component.ts
+++ b/src/viewerModule/nehuba/ngLayerCtl/ngLayerCtrl.component.ts
@@ -73,6 +73,8 @@ export class NgLayerCtrlCmp implements OnChanges, OnDestroy{
   visible: boolean = true
   private viewer: NehubaViewerUnit
 
+  private exportNehuba: any
+
   constructor(
     private store: Store<any>,
     @Inject(NEHUBA_INSTANCE_INJTKN) nehubaViewer$: Observable<NehubaViewerUnit>
@@ -81,6 +83,8 @@ export class NgLayerCtrlCmp implements OnChanges, OnDestroy{
     this.onDestroyCb.push(
       () => sub.unsubscribe()
     )
+
+    getExportNehuba().then(exportNehuba => this.exportNehuba = exportNehuba)
   }
 
   ngOnDestroy(): void {
@@ -121,7 +125,7 @@ export class NgLayerCtrlCmp implements OnChanges, OnDestroy{
   }
 
   setOrientation(): void {
-    const { mat4, quat, vec3 } = getExportNehuba()
+    const { mat4, quat, vec3 } = this.exportNehuba
 
     /**
      * glMatrix seems to store the matrix in transposed format
diff --git a/src/viewerModule/nehuba/store/type.ts b/src/viewerModule/nehuba/store/type.ts
index d45347a166b1630d9ecc47cebbfaa48a3a95985b..a55be8ced700fb825deb317904573577a0c8c7fb 100644
--- a/src/viewerModule/nehuba/store/type.ts
+++ b/src/viewerModule/nehuba/store/type.ts
@@ -12,7 +12,7 @@ export interface IAuxMesh {
 
 
 export interface INehubaFeature {
-  layers: atlasAppearance.NgLayerCustomLayer[]
+  layers: atlasAppearance.const.NgLayerCustomLayer[]
   panelMode: string
   panelOrder: string
   octantRemoval: boolean
diff --git a/src/viewerModule/nehuba/userLayers/service.ts b/src/viewerModule/nehuba/userLayers/service.ts
index 607f01558d663ad9c5502ad4a25b7d926b40cc32..042cc54851be1e31cf457926d81768dfcbc2a5a8 100644
--- a/src/viewerModule/nehuba/userLayers/service.ts
+++ b/src/viewerModule/nehuba/userLayers/service.ts
@@ -10,8 +10,7 @@ import {
 } from "src/atlasComponents/sapi/core/space/interspaceLinearXform"
 import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.service"
 import { RouterService } from "src/routerModule/router.service"
-import { atlasAppearance } from "src/state"
-import { NgLayerCustomLayer } from "src/state/atlasAppearance"
+import * as atlasAppearance from "src/state/atlasAppearance"
 import { EnumColorMapName } from "src/util/colorMaps"
 import { getShader } from "src/util/constants"
 import { getExportNehuba, getUuid } from "src/util/fn"
@@ -46,7 +45,7 @@ export class UserLayerService implements OnDestroy {
   async getCvtFileToUrl(file: File): Promise<{
     url: string
     meta: Meta
-    options?: Omit<NgLayerCustomLayer, OmitKeys>
+    options?: Omit<atlasAppearance.const.NgLayerCustomLayer, OmitKeys>
   }> {
     /**
      * if extension is .swc, process as if swc
@@ -90,7 +89,8 @@ export class UserLayerService implements OnDestroy {
     const buf = await file.arrayBuffer()
     let outbuf
     try {
-      outbuf = getExportNehuba().pako.inflate(buf).buffer
+      const { pako } = await getExportNehuba()
+      outbuf = pako.inflate(buf).buffer
     } catch (e) {
       console.log("unpack error", e)
       outbuf = buf
@@ -128,14 +128,14 @@ export class UserLayerService implements OnDestroy {
   addUserLayer(
     url: string,
     meta: Meta,
-    options: Omit<NgLayerCustomLayer, OmitKeys> = {}
+    options: Omit<atlasAppearance.const.NgLayerCustomLayer, OmitKeys> = {}
   ) {
     this.verifyUrl(url)
     if (this.userLayerUrlToIdMap.has(url)) {
       throw new Error(`url ${url} already added`)
     }
     const id = getUuid()
-    const layer: NgLayerCustomLayer = {
+    const layer: atlasAppearance.const.NgLayerCustomLayer = {
       id,
       clType: "customlayer/nglayer",
       source: url,
diff --git a/src/viewerModule/nehuba/util.ts b/src/viewerModule/nehuba/util.ts
index 1073c1bde8e78db2c490b44ab6bc17c65b8aab92..81aed3560abe6fe1f41b9faf13211d1002702591 100644
--- a/src/viewerModule/nehuba/util.ts
+++ b/src/viewerModule/nehuba/util.ts
@@ -181,8 +181,8 @@ export const isIdentityQuat = (ori: number[]): boolean => Math.abs(ori[0]) < 1e-
 
 export const importNehubaFactory = (appendSrc: (src: string) => Promise<void>): () => Promise<void> => {
   let pr: Promise<void>
-  return () => {
-    if (getExportNehuba()) return Promise.resolve()
+  return async () => {
+    if (!!(window as any).export_nehuba) return 
 
     if (pr) return pr
     pr = appendSrc('main.bundle.js')
diff --git a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts
index a03768e58f463917aae98d0ba492ef238d96e62f..7d6634342b3daf28b948e97a7d6fcca9d6b9310f 100644
--- a/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts
+++ b/src/viewerModule/threeSurfer/threeSurferGlue/threeSurfer.component.ts
@@ -1,7 +1,7 @@
 import { Component, Output, EventEmitter, ElementRef, OnDestroy, AfterViewInit, Inject, Optional, ChangeDetectionStrategy } from "@angular/core";
 import { EnumViewerEvt, IViewer, TViewerEvent } from "src/viewerModule/viewer.interface";
-import { combineLatest, forkJoin, from, merge, Observable, Subject } from "rxjs";
-import { debounceTime, distinctUntilChanged, filter, map, scan, shareReplay, switchMap } from "rxjs/operators";
+import { combineLatest, from, merge, NEVER, Observable, Subject } from "rxjs";
+import { catchError, debounceTime, distinctUntilChanged, filter, map, scan, shareReplay, switchMap } from "rxjs/operators";
 import { ComponentStore } from "src/viewerModule/componentStore";
 import { select, Store } from "@ngrx/store";
 import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
@@ -145,15 +145,12 @@ export class ThreeSurferGlueCmp implements IViewer<'threeSurfer'>, AfterViewInit
       select(atlasSelection.selectors.selectedParcAllRegions),
     )
   ]).pipe(
-    switchMap(([ { atlas, parcellation, template },  regions]) => {
-      const returnObj = {
-        'left': {} as Record<number, SxplrRegion>,
-        'right': {} as Record<number, SxplrRegion>
-      }
+    switchMap(([ { parcellation, template },  regions]) => {
       return merge(
         ...regions.map(region => 
           from(this.sapi.getRegionLabelIndices(template, parcellation, region)).pipe(
-            map(label => ({ region, label }))
+            map(label => ({ region, label })),
+            catchError(() => NEVER)
           )
         )
       ).pipe(
diff --git a/src/viewerModule/viewer.common.effects.ts b/src/viewerModule/viewer.common.effects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cc888f129935050edd49a5238764a672f3d00d66
--- /dev/null
+++ b/src/viewerModule/viewer.common.effects.ts
@@ -0,0 +1,53 @@
+import { Injectable } from "@angular/core";
+import { createEffect } from "@ngrx/effects";
+import { select, Store } from "@ngrx/store";
+import { forkJoin, of } from "rxjs";
+import { distinctUntilChanged, map, switchMap } from "rxjs/operators";
+import * as atlasSelection from "src/state/atlasSelection";
+import * as atlasAppearance from "src/state/atlasAppearance"
+import { SAPI } from "src/atlasComponents/sapi";
+
+@Injectable()
+export class ViewerCommonEffects {
+  onATPSetUseViewer$ = createEffect(() => this.store.pipe(
+    select(atlasSelection.selectors.standaloneVolumes),
+    distinctUntilChanged((o, n) => o?.length === n?.length),
+    switchMap(volumes => volumes?.length > 0
+      ? of( atlasAppearance.const.useViewer.NEHUBA )
+      : this.store.pipe(
+        select(atlasSelection.selectors.selectedTemplate),
+        distinctUntilChanged((o, n) => o?.id === n?.id),
+        switchMap(template => {
+          if (!template) {
+            return of(null as atlasAppearance.const.UseViewer)
+          }
+          return forkJoin({
+            voxel: this.sapi.getVoxelTemplateImage(template),
+            surface: this.sapi.getSurfaceTemplateImage(template)
+          }).pipe(
+            map(vols => {
+              if (!vols) return null
+              const { voxel, surface } = vols
+              if (voxel.length > 0 && surface.length > 0) {
+                console.error(`both voxel and surface length are > 0, this should not happen.`)
+                return atlasAppearance.const.useViewer.NOT_SUPPORTED
+              }
+              if (voxel.length > 0) {
+                return atlasAppearance.const.useViewer.NEHUBA
+              }
+              if (surface.length > 0) {
+                return atlasAppearance.const.useViewer.THREESURFER
+              }
+              return atlasAppearance.const.useViewer.NOT_SUPPORTED
+            })
+          )
+        })
+      )
+    ),
+    map(viewer => atlasAppearance.actions.setUseViewer({ viewer }))
+  ))
+
+  constructor(private store: Store, private sapi: SAPI){
+
+  }
+}
diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts
index db9b4f7e518b9ecb288f041d5ce9c8a1f46653ee..25c57644e627082617ed60ce3d5262def63be2d8 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.component.ts
+++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts
@@ -1,7 +1,7 @@
 import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, TemplateRef, ViewChild, ViewContainerRef } from "@angular/core";
 import { select, Store } from "@ngrx/store";
-import { combineLatest, forkJoin, Observable, of, Subscription } from "rxjs";
-import { debounceTime, distinctUntilChanged, map, shareReplay, startWith, switchMap } from "rxjs/operators";
+import { combineLatest, Observable, Subscription } from "rxjs";
+import { debounceTime, map, shareReplay, startWith } from "rxjs/operators";
 import { CONST, ARIA_LABELS, QUICKTOUR_DESC } from 'common/constants'
 import { animate, state, style, transition, trigger } from "@angular/animations";
 import { IQuickTourData } from "src/ui/quickTour";
@@ -9,8 +9,8 @@ import { EnumViewerEvt, TContextArg, TSupportedViewers, TViewerEvent } from "../
 import { ContextMenuService, TContextMenuReg } from "src/contextMenuModule";
 import { DialogService } from "src/services/dialogService.service";
 import { SAPI } from "src/atlasComponents/sapi";
-import { Feature, NgLayerSpec, SxplrAtlas, SxplrRegion, TThreeMesh } from "src/atlasComponents/sapi/sxplrTypes"
-import { atlasSelection, userInteraction } from "src/state";
+import { Feature, SxplrAtlas, SxplrRegion } from "src/atlasComponents/sapi/sxplrTypes"
+import { atlasAppearance, atlasSelection, userInteraction } from "src/state";
 
 import { environment } from "src/environments/environment"
 // import { SapiViewsFeaturesVoiQuery } from "src/atlasComponents/sapiViews/features";
@@ -115,58 +115,19 @@ export class ViewerCmp implements OnDestroy {
     select(atlasSelection.selectors.selectedParcAllRegions)
   )
 
-  public isStandaloneVolumes$ = this.store$.pipe(
-    select(atlasSelection.selectors.standaloneVolumes),
-    map(v => v.length > 0)
-  )
-
   public viewerMode$: Observable<string> = this.store$.pipe(
     select(atlasSelection.selectors.viewerMode),
     shareReplay(1),
   )
 
-  public useViewer$: Observable<TSupportedViewers | 'notsupported'> = combineLatest([
-    this.store$.pipe(
-      atlasSelection.fromRootStore.distinctATP(),
-      switchMap(({ template }) => template
-        ? forkJoin({
-          voxel: this.sapi.getVoxelTemplateImage(template),
-          surface: this.sapi.getSurfaceTemplateImage(template),
-        })
-        : of(null as { voxel: NgLayerSpec[], surface: TThreeMesh[] })
-      ),
-      map(vols => {
-        if (!vols) return null
-        const flags = {
-          isNehuba: false,
-          isThreeSurfer: false
-        }
-        if (vols.voxel.length > 0) {
-          flags.isNehuba = true
-        }
-
-        if (vols.surface.length > 0) {
-          flags.isThreeSurfer = true
-        }
-        return flags
-      })
-    ),
-    this.isStandaloneVolumes$,
-  ]).pipe(
-    distinctUntilChanged(([ prevFlags, prevIsSv ], [  currFlags, currIsSv ]) => {
-      const same = prevIsSv === currIsSv
-      && prevFlags?.isNehuba === currFlags?.isNehuba
-      && prevFlags?.isThreeSurfer === currFlags?.isThreeSurfer
-      return same
-    }),
-    map<unknown, TSupportedViewers | 'notsupported'>(([flags, isSv]) => {
-      if (isSv) return 'nehuba'
-      if (!flags) return null
-      if (flags.isNehuba) return 'nehuba'
-      if (flags.isThreeSurfer) return 'threeSurfer'
-      return 'notsupported'
-    }),
-    shareReplay(1),
+  public useViewer$: Observable<TSupportedViewers | 'notsupported'> = this.store$.pipe(
+    select(atlasAppearance.selectors.useViewer),
+    map(useviewer => {
+      if (useviewer === "NEHUBA") return "nehuba"
+      if (useviewer === "THREESURFER") return "threeSurfer"
+      if (useviewer === "NOT_SUPPORTED") return "notsupported"
+      return null
+    })
   )
 
   public viewerCtx$ = this.ctxMenuSvc.context$