diff --git a/src/atlasViewer/atlasViewer.constantService.service.spec.ts b/src/atlasViewer/atlasViewer.constantService.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..71c9ecd02371900c4e3403e024f9d6e9c648a153
--- /dev/null
+++ b/src/atlasViewer/atlasViewer.constantService.service.spec.ts
@@ -0,0 +1,110 @@
+import { encodeNumber, decodeToNumber } from './atlasViewer.constantService.service'
+import {} from 'jasmine'
+
+const FLOAT_PRECISION = 6
+
+describe('encodeNumber/decodeToNumber', () => {
+
+
+  const getCompareOriginal = (original: number[]) => (element:string, index: number) => 
+    original[index].toString().length >= element.length
+  
+
+  const lengthShortened = (original: number[], encodedString: string[]) =>
+    encodedString.every(getCompareOriginal(original))
+
+  it('should encode/decode positive integer as expected', () => {
+
+    const positiveInt = [
+      0,
+      1,
+      99999999999,
+      12347
+    ]
+
+    const encodedString = positiveInt.map(n => encodeNumber(n))
+    const decodedString = encodedString.map(s => decodeToNumber(s))
+    expect(decodedString).toEqual(positiveInt)
+    
+    expect(lengthShortened(positiveInt, encodedString)).toBe(true)
+  })
+
+  it('should encode/decode ANY positive integer as expected', () => {
+    const posInt = Array(1000).fill(null).map(() => {
+      const numDig = Math.ceil(Math.random() * 7)
+      return Math.floor(Math.random() * Math.pow(10, numDig))
+    })
+    const encodedString = posInt.map(n => encodeNumber(n))
+    const decodedNumber = encodedString.map(s => decodeToNumber(s))
+    expect(decodedNumber).toEqual(posInt)
+
+    expect(lengthShortened(posInt, encodedString)).toBe(true)
+  })
+
+
+  it('should encode/decode signed integer as expected', () => {
+
+    const signedInt = [
+      0,
+      -0,
+      -1,
+      1,
+      128,
+      -54
+    ]
+  
+    const encodedString = signedInt.map(n => encodeNumber(n))
+    const decodedNumber = encodedString.map(s => decodeToNumber(s))
+
+    /**
+     * -0 will be converted to 0 by the encode/decode process, but does not deep equal, according to jasmine
+     */
+    expect(decodedNumber).toEqual(signedInt.map(v => v === 0 ? 0 : v))
+
+    expect(lengthShortened(signedInt, encodedString)).toBe(true)
+  })
+
+  it('should encode/decode ANY signed integer as expected', () => {
+
+    const signedInt = Array(1000).fill(null).map(() => {
+      const numDig = Math.ceil(Math.random() * 7)
+      return Math.floor(Math.random() * Math.pow(10, numDig)) * (Math.random() > 0.5 ? 1 : -1)
+    })
+    const encodedString = signedInt.map(n => encodeNumber(n))
+    const decodedNumber = encodedString.map(s => decodeToNumber(s))
+
+    /**
+     * -0 will be converted to 0 by the encode/decode process, but does not deep equal, according to jasmine
+     */
+    expect(decodedNumber).toEqual(signedInt.map(v => v === 0 ? 0 : v))
+
+    expect(lengthShortened(signedInt, encodedString)).toBe(true)
+  })
+
+
+  it('should encode/decode float as expected', () => {
+    const floatNum = [
+      0.111,
+      12.23,
+      1723.0
+    ]
+
+    const encodedString = floatNum.map(f => encodeNumber(f, { float: true }))
+    const decodedNumber = encodedString.map(s => decodeToNumber(s, { float: true }))
+    expect(decodedNumber.map(n => n.toFixed(FLOAT_PRECISION))).toEqual(floatNum.map(n => n.toFixed(FLOAT_PRECISION)))
+  })
+
+  it('should encode/decode ANY float as expected', () => {
+    const floatNums = Array(1000).fill(null).map(() => {
+      const numDig = Math.ceil(Math.random() * 7)
+      return (Math.random() > 0.5 ? 1 : -1) * Math.floor(
+        Math.random() * Math.pow(10, numDig)
+      )
+    })
+
+    const encodedString = floatNums.map(f => encodeNumber(f, { float: true }))
+    const decodedNumber = encodedString.map(s => decodeToNumber(s, { float: true }))
+
+    expect(floatNums.map(v => v.toFixed(FLOAT_PRECISION))).toEqual(decodedNumber.map(n => n.toFixed(FLOAT_PRECISION)))
+  })
+})
\ No newline at end of file
diff --git a/src/atlasViewer/atlasViewer.constantService.service.ts b/src/atlasViewer/atlasViewer.constantService.service.ts
index 75a157e0c68ae679c2ce4dde61b83179972a5125..a452c83b0d81b948ff923994f2fa5952ab9e9146 100644
--- a/src/atlasViewer/atlasViewer.constantService.service.ts
+++ b/src/atlasViewer/atlasViewer.constantService.service.ts
@@ -290,4 +290,98 @@ export const SUPPORT_LIBRARY_MAP : Map<string,HTMLElement> = new Map([
   ['vue@2.5.16',parseURLToElement('https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js')],
   ['preact@8.4.2',parseURLToElement('https://cdn.jsdelivr.net/npm/preact@8.4.2/dist/preact.min.js')],
   ['d3@5.7.0',parseURLToElement('https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js')]
-])
\ No newline at end of file
+])
+
+/**
+ * First attempt at encoding int (e.g. selected region, navigation location) from number (loc info density) to b64 (higher info density)
+ * The constraint is that the cipher needs to be commpatible with URI encoding
+ * and a URI compatible separator is required. 
+ * 
+ * The implementation below came from 
+ * https://stackoverflow.com/a/6573119/6059235
+ * 
+ * While a faster solution exist in the same post, this operation is expected to be done:
+ * - once per 1 sec frequency
+ * - on < 1000 numbers
+ * 
+ * So performance is not really that important (Also, need to learn bitwise operation)
+ */
+
+const cipher = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-'
+export const separator = "."
+const negString = '~'
+
+const encodeInt = (number: number) => {
+  if (number % 1 !== 0) throw 'cannot encodeInt on a float. Ensure float flag is set'
+  if (isNaN(Number(number)) || number === null || number === Number.POSITIVE_INFINITY)
+    throw 'The input is not valid'
+
+  let rixit // like 'digit', only in some non-decimal radix 
+  let residual
+  let result = ''
+
+  if (number < 0) {
+    result += negString
+    residual = Math.floor(number * -1)
+  } else {
+    residual = Math.floor(number)
+  }
+
+  while (true) {
+    rixit = residual % 64
+    // console.log("rixit : " + rixit)
+    // console.log("result before : " + result)
+    result = cipher.charAt(rixit) + result
+    // console.log("result after : " + result)
+    // console.log("residual before : " + residual)
+    residual = Math.floor(residual / 64)
+    // console.log("residual after : " + residual)
+
+    if (residual == 0)
+      break;
+    }
+  return result
+}
+
+interface B64EncodingOption {
+  float: boolean
+}
+
+const defaultB64EncodingOption = {
+  float: false
+}
+
+export const encodeNumber: (number:number, option?: B64EncodingOption) => string = (number: number, { float = false }: B64EncodingOption = defaultB64EncodingOption) => {
+  if (!float) return encodeInt(number)
+  else {
+    const floatArray = new Float32Array(1)
+    floatArray[0] = number
+    const intArray = new Uint32Array(floatArray.buffer)
+    const castedInt = intArray[0]
+    return encodeInt(castedInt)
+  }
+}
+
+const decodetoInt = (encodedString: string) => {
+  let _encodedString, negFlag = false
+  if (encodedString.slice(-1) === negString) {
+    negFlag = true
+    _encodedString = encodedString.slice(0, -1)
+  } else {
+    _encodedString = encodedString
+  }
+  return (negFlag ? -1 : 1) * [..._encodedString].reduce((acc,curr) => {
+    return acc * 64 + cipher.indexOf(curr)
+  }, 0)
+}
+
+export const decodeToNumber: (encodedString:string, option?: B64EncodingOption) => number = (encodedString: string, {float = false} = defaultB64EncodingOption) => {
+  if (!float) return decodetoInt(encodedString)
+  else {
+    const _int = decodetoInt(encodedString)
+    const intArray = new Uint32Array(1)
+    intArray[0] = _int
+    const castedFloat = new Float32Array(intArray.buffer)
+    return castedFloat[0]
+  }
+}
\ No newline at end of file
diff --git a/src/atlasViewer/atlasViewer.urlService.service.ts b/src/atlasViewer/atlasViewer.urlService.service.ts
index b548c96f292bfa89fa808beccd7b2c5b16778a2a..8506cf25f696ab09fbe69dd4647c8d650b5dc6de 100644
--- a/src/atlasViewer/atlasViewer.urlService.service.ts
+++ b/src/atlasViewer/atlasViewer.urlService.service.ts
@@ -1,11 +1,11 @@
 import { Injectable } from "@angular/core";
 import { Store, select } from "@ngrx/store";
-import { ViewerStateInterface, isDefined, NEWVIEWER, CHANGE_NAVIGATION, ADD_NG_LAYER, generateLabelIndexId } from "../services/stateStore.service";
+import { ViewerStateInterface, isDefined, NEWVIEWER, CHANGE_NAVIGATION, ADD_NG_LAYER } from "../services/stateStore.service";
 import { PluginInitManifestInterface } from 'src/services/state/pluginState.store'
 import { Observable,combineLatest } from "rxjs";
 import { filter, map, scan, distinctUntilChanged, skipWhile, take } from "rxjs/operators";
 import { PluginServices } from "./atlasViewer.pluginService.service";
-import { AtlasViewerConstantsServices } from "./atlasViewer.constantService.service";
+import { AtlasViewerConstantsServices, encodeNumber, separator, decodeToNumber } from "./atlasViewer.constantService.service";
 import { ToastService } from "src/services/toastService.service";
 import { SELECT_REGIONS_WITH_ID } from "src/services/state/viewerState.store";
 
@@ -148,6 +148,9 @@ export class AtlasViewerURLService{
         /**
          * either or both parcellationToLoad and .regions maybe empty
          */
+        /**
+         * backwards compatibility
+         */
         const selectedRegionsParam = searchparams.get('regionsSelected')
         if(selectedRegionsParam){
           const ids = selectedRegionsParam.split('_')
@@ -157,6 +160,34 @@ export class AtlasViewerURLService{
             selectRegionIds: ids
           })
         }
+
+        const cRegionsSelectedParam = searchparams.get('cRegionsSelected')
+        if (cRegionsSelectedParam) {
+          try {
+            const json = JSON.parse(cRegionsSelectedParam)
+  
+            const selectRegionIds = []
+  
+            for (let ngId in json) {
+              const val = json[ngId]
+              const labelIndicies = val.split(separator).map(n =>decodeToNumber(n))
+              for (let labelIndex of labelIndicies) {
+                selectRegionIds.push(`${ngId}#${labelIndex}`)
+              }
+            }
+  
+            this.store.dispatch({
+              type: SELECT_REGIONS_WITH_ID,
+              selectRegionIds
+            })
+  
+          } catch (e) {
+            /**
+             * parsing cRegionSelected error
+             */
+            console.log('parsing cRegionSelected error', e)
+          }
+        }
       }
       
       /* now that the parcellation is loaded, load the navigation state */
@@ -175,6 +206,26 @@ export class AtlasViewerURLService{
         })
       }
 
+      const cViewerState = searchparams.get('cNavigation')
+      if (cViewerState) {
+        const [ cO, cPO, cPZ, cP, cZ ] = cViewerState.split(`${separator}${separator}`)
+        const o = cO.split(separator).map(s => decodeToNumber(s, {float: true}))
+        const po = cPO.split(separator).map(s => decodeToNumber(s, {float: true}))
+        const pz = decodeToNumber(cPZ)
+        const p = cP.split(separator).map(s => decodeToNumber(s))
+        const z = decodeToNumber(cZ)
+        this.store.dispatch({
+          type : CHANGE_NAVIGATION,
+          navigation : {
+            orientation: o,
+            perspectiveOrientation: po,
+            perspectiveZoom: pz,
+            position: p,
+            zoom: z
+          }
+        })
+      }
+
       const niftiLayers = searchparams.get('niftiLayers')
       if(niftiLayers){
         const layers = niftiLayers.split('__')
@@ -213,18 +264,56 @@ export class AtlasViewerURLService{
                     isDefined(state[key].position) &&
                     isDefined(state[key].zoom)
                   ){
-                    _[key] = [
-                      state[key].orientation.join('_'),
-                      state[key].perspectiveOrientation.join('_'),
-                      state[key].perspectiveZoom,
-                      state[key].position.join('_'),
-                      state[key].zoom 
-                    ].join('__')
+                    const {
+                      orientation, 
+                      perspectiveOrientation, 
+                      perspectiveZoom, 
+                      position, 
+                      zoom
+                    } = state[key]
+
+                    _['cNavigation'] = [
+                      orientation.map(n => encodeNumber(n, {float: true})).join(separator),
+                      perspectiveOrientation.map(n => encodeNumber(n, {float: true})).join(separator),
+                      encodeNumber(Math.floor(perspectiveZoom)),
+                      Array.from(position).map((v:number) => Math.floor(v)).map(n => encodeNumber(n)).join(separator),
+                      encodeNumber(Math.floor(zoom)) 
+                    ].join(`${separator}${separator}`)
+                    
+                    _[key] = null
                   }
                   break;
-                case 'regionsSelected':
-                  _[key] = state[key].map(({ ngId, labelIndex })=> generateLabelIndexId({ ngId,labelIndex })).join('_')
+                case 'regionsSelected': {
+                  // _[key] = state[key].map(({ ngId, labelIndex })=> generateLabelIndexId({ ngId,labelIndex })).join('_')
+                  const ngIdLabelIndexMap : Map<string, number[]> = state[key].reduce((acc, curr) => {
+                    const returnMap = new Map(acc)
+                    const { ngId, labelIndex } = curr
+                    const existingArr = (returnMap as Map<string, number[]>).get(ngId)
+                    if (existingArr) {
+                      existingArr.push(labelIndex)
+                    } else {
+                      returnMap.set(ngId, [labelIndex])
+                    }
+                    return returnMap
+                  }, new Map())
+
+                  if (ngIdLabelIndexMap.size === 0) {
+                    _['cRegionsSelected'] = null
+                    _[key] = null
+                    break;
+                  }
+                  
+                  const returnObj = {}
+
+                  for (let entry of ngIdLabelIndexMap) {
+                    const [ ngId, labelIndicies ] = entry
+                    returnObj[ngId] = labelIndicies.map(n => encodeNumber(n)).join(separator)
+                  }
+                  
+                  _['cRegionsSelected'] = JSON.stringify(returnObj)
+                  _[key] = null
                   break;
+                }
                 case 'templateSelected':
                 case 'parcellationSelected':
                   _[key] = state[key].name