diff --git a/NOMADVRLib/ConfigFile.cpp b/NOMADVRLib/ConfigFile.cpp
index 63976160203008cd48beb901b315eca7ba38a95a..549af326797f09b289d8b6eabc3de93bc3f0bf96 100644
--- a/NOMADVRLib/ConfigFile.cpp
+++ b/NOMADVRLib/ConfigFile.cpp
@@ -78,8 +78,9 @@ const char * loadConfigFileErrors[] =
 	"markercolours with no previous correct timesteps parameter", //-18
 	"Error reading atomcolour", // -19
 	"Error loading xyz file, add 100 to see the error",//<-100
-	"Error loading cube file, add 100 to see the error",//<-200
-	"Error loading json file, add 200 to see the error",//<-300
+	"Error loading cube file, add 200 to see the error",//<-200
+	"Error loading encyclopedia json file, add 300 to see the error",//<-300
+	"Error loading analytics json file, add 400 to see the error",//<-400
 };
 
 void updateTIMESTEPS (int timesteps)
@@ -180,6 +181,7 @@ int loadConfigFile(const char * f)
 	screenshotdownscaling=1;
 	hapticFeedback=false;
 	showcontrollers=false;
+	inv_abc_init=false;
 	//
 	FILE *F = fopen(f, "r");
 	if (F == 0)
@@ -328,7 +330,7 @@ int loadConfigFile(const char * f)
 				}
 			has_abc = true;
 		}
-		else if (!strcmp(s, "json")) {
+		else if (!strcmp(s, "json") || !strcmp (s, "encyclopediajson")) {
 			r=readString(F, s);
 			if (r!=0)
 				return -14;
@@ -344,7 +346,24 @@ int loadConfigFile(const char * f)
 			numClonedAtoms=clonedAtoms[0].size()/4;
 			has_abc=true;
 			updateTIMESTEPS (timesteps);
-		} 
+		}
+		else if (!strcmp(s, "analyticsjson")) {
+			r = readString(F, s);
+			if (r != 0)
+				return -14;
+			char file[256];
+			sprintf(file, "%s%s", PATH, s);
+			fixFile(file);
+			int e;
+			int timesteps;
+			//rgh fixme, we know only one
+			e = readAtomsAnalyticsJson(file, &numAtoms, &timesteps, &atoms, abc, &clonedAtoms);
+			if (e<0)
+				return e - 400;
+			numClonedAtoms = clonedAtoms[0].size() / 4;
+			has_abc = true;
+			updateTIMESTEPS(timesteps);
+		}
 		else if (!strcmp(s, "baseurl")) {
 			r=readString (F, base_url);
 			if (r!=0)
diff --git a/NOMADVRLib/IsosurfacesGL.cpp b/NOMADVRLib/IsosurfacesGL.cpp
index f1c0ac93d9eac0a0c038614c53b03380e0ef1f08..16997994eee2700bc8a41c84676825622813edcb 100644
--- a/NOMADVRLib/IsosurfacesGL.cpp
+++ b/NOMADVRLib/IsosurfacesGL.cpp
@@ -2,6 +2,7 @@
 
 #include <vector>
 #include <math.h>
+#include <stdint.h>
 
 #include "MyGL.h"
 #include "eprintf.h"
diff --git a/NOMADVRLib/atoms.cpp b/NOMADVRLib/atoms.cpp
index 354cf0d722a8c14c9e071b6b08a191c3f2c691fe..957e559e64a4987cfcfd9ebabd45fc174f3a74ab 100644
--- a/NOMADVRLib/atoms.cpp
+++ b/NOMADVRLib/atoms.cpp
@@ -24,6 +24,11 @@ const char * TMPDIR;//filled by main
 //="/storage/540E-1AE2/";
 #endif
 
+bool inv_abc_init=false;
+float inv_abc[3][3];
+
+
+
 const char * const atomNames[] =
 
 {
@@ -493,7 +498,7 @@ return readAtomsJson (cmd, numatoms, timesteps, pos, abc, clonedAtoms, token);
 
 bool  isAlmostZero(float coordinate) 
 {   
-	return (coordinate < 1E-5);
+	return (fabs(coordinate) < 1E-5);
 }
 
 void add (std::vector<float> *v, float x, float y, float z, float a)
@@ -504,6 +509,71 @@ void add (std::vector<float> *v, float x, float y, float z, float a)
 	v->push_back(a);
 }
 
+const char * readAtomsAnalyticsJsonErrors[] = {
+	"All Ok",//0
+	"Could not open file", //-1
+	"atom_species not in json", //-2
+	"json parse error or no atom_positions", //-3
+	"lattice_vectors not in json", //-4
+};
+
+int readAtomsAnalyticsJson(const char *const f, int **numatoms, int *timesteps, float ***pos, float abc[3][3],
+	std::vector<float>** clonedAtoms)
+{
+	float tmppos[3];
+
+	FILE *fp = fopen(f, "r");
+	if (fp == 0) {
+		eprintf("readAtomsAnalyticsJson, could not open file %s", f);
+		return -1;
+	}
+	char readBuffer[65536];
+	rapidjson::FileReadStream is(fp, readBuffer, sizeof(readBuffer));
+	rapidjson::Document json;
+	json.ParseStream(is);
+	fclose(fp);
+	*timesteps = 1;
+	*pos = new float*[*timesteps];
+	*numatoms = new int[*timesteps];
+	*clonedAtoms=new std::vector<float>[*timesteps];
+	if (!json.HasParseError() && json.HasMember("atom_positions")) {
+		if (json.HasMember("lattice_vectors")) {
+			const rapidjson::GenericValue<rapidjson::UTF8<> > &lv = json["lattice_vectors"];
+			const rapidjson::GenericValue<rapidjson::UTF8<> > &flatdata = lv["flatData"];
+			for (int i=0;i<3;i++)
+				for (int j=0;j<3;j++)
+					abc[i][j]=flatdata[i*3+j].GetFloat()*1e10; // meter -> aangstrom
+		} else return (-4);
+		const rapidjson::GenericValue<rapidjson::UTF8<> > &result = json["atom_positions"];
+		if (!result.HasMember("shape"))
+			return (-1);
+		const rapidjson::GenericValue<rapidjson::UTF8<> > &shape = result["shape"]; //[numatoms, 3]
+		**numatoms = shape[0].GetInt();
+		(*pos)[0] = new float[**numatoms * 4];
+		if (json.HasMember("atom_species")) {
+		const rapidjson::GenericValue<rapidjson::UTF8<> > &results = json["atom_species"];
+		for (int i = 0; i < **numatoms ; i++)
+			(*pos)[0][i*4+3]=results[i].GetInt()-1;
+
+
+		//atoms are stored in space coordinates, in meters
+		//rgh FIXME: disable cloned atoms for now
+		const rapidjson::GenericValue<rapidjson::UTF8<> > &flatdata = result["flatData"];
+		for (int i = 0; i < **numatoms ; i++) {
+			for (int j=0;j<3;j++)
+				(*pos)[0][i*4+j] = flatdata[i*3+j].GetFloat()*1e10; //we store them in aangstrom
+			CloneSpatialAtoms((*pos)[0], (*pos)[0][3], *clonedAtoms);
+		}
+	}
+	else return (-2);
+
+	}
+	else return (-3);
+
+	TransformAtoms(*clonedAtoms, abc);
+	return 0;
+}
+
 int readAtomsJson (const char *const f, int **numatoms, int *timesteps, float ***pos, float abc[3][3], 
 				   std::vector<float>** clonedAtoms, const char *const token)
 {
@@ -600,13 +670,14 @@ int readAtomsJson (const char *const f, int **numatoms, int *timesteps, float **
 			(**pos)[4*i+s]=tmppos[0]*abc[0][s]+tmppos[1]*abc[1][s]+tmppos[2]*abc[2][s];
 
 	
-		}
-		TransformAtoms(*clonedAtoms, abc);
+	}
+	TransformAtoms(*clonedAtoms, abc);
 
 //eprintf ("readAtomsJson, end");
 return 0;
 }
 
+//from abc coordinates to spatial coordinates
 void TransformAtoms(std::vector<float>* clonedAtoms, const float abc[3][3])
 {
 	float tmppos[3];
@@ -618,6 +689,57 @@ void TransformAtoms(std::vector<float>* clonedAtoms, const float abc[3][3])
 	}
 }
 
+//returns false if abc is not an invertible matrix (should not happen)
+bool CloneSpatialAtoms (float tmppos[3], float k, std::vector<float>* clonedAtoms) 
+{ //move atoms to abc coordinates, then Clone()
+
+	if (!inv_abc_init) {
+		inv_abc_init=true;
+		//Contains code from OpenVR samples/shared/Matrices.cpp under the
+		//BSD 3-clause "New" or "Revised" License
+		//https://github.com/ValveSoftware/openvr/blob/master/LICENSE
+		float tmp[9];
+		float determinant;
+
+    tmp[0] = abc[1][1] * abc[2][2] - abc[1][2] * abc[2][1];
+    tmp[1] = abc[0][2] * abc[2][1] - abc[0][1] * abc[2][2];
+    tmp[2] = abc[0][1] * abc[1][2] - abc[0][2] * abc[1][1];
+    tmp[3] = abc[1][2] * abc[2][0] - abc[1][0] * abc[2][2];
+    tmp[4] = abc[0][0] * abc[2][2] - abc[0][2] * abc[2][0];
+    tmp[5] = abc[0][2] * abc[1][0] - abc[0][0] * abc[1][2];
+    tmp[6] = abc[1][0] * abc[2][1] - abc[1][1] * abc[2][0];
+    tmp[7] = abc[0][1] * abc[2][0] - abc[0][0] * abc[2][1];
+    tmp[8] = abc[0][0] * abc[1][1] - abc[0][1] * abc[1][0];
+
+    // check determinant if it is 0
+    determinant = abc[0][0] * tmp[0] + abc[0][1] * tmp[3] + abc[0][2] * tmp[6];
+    if(fabs(determinant) <= 1e-5)
+    {
+        return false; // cannot inverse, make it idenety matrix
+    }
+
+    // divide by the determinant
+    float invDeterminant = 1.0f / determinant;
+    inv_abc[0][0] = invDeterminant * tmp[0];
+    inv_abc[0][1] = invDeterminant * tmp[1];
+    inv_abc[0][2] = invDeterminant * tmp[2];
+    inv_abc[1][0] = invDeterminant * tmp[3];
+    inv_abc[1][1] = invDeterminant * tmp[4];
+    inv_abc[1][2] = invDeterminant * tmp[5];
+    inv_abc[2][0] = invDeterminant * tmp[6];
+    inv_abc[2][1] = invDeterminant * tmp[7];
+    inv_abc[2][2] = invDeterminant * tmp[8];
+	}
+
+	float t[3];
+	for (int s=0;s<3;s++)
+			t[s]=tmppos[0]*inv_abc[0][s]+tmppos[1]*inv_abc[1][s]+tmppos[2]*inv_abc[2][s];
+
+	Clone (t, k, clonedAtoms);
+	return true;
+}
+
+//tmppos in abc coordinates
 void Clone (float tmppos[3], float k, std::vector<float>* clonedAtoms) 
 {
 bool iaz[3];
diff --git a/NOMADVRLib/atoms.hpp b/NOMADVRLib/atoms.hpp
index 7c6a7434630a4a85bd22422bcd4f0e0c9b85aaa2..ca4c71a6ef8ecceb9c77a9d35722153a6f850105 100644
--- a/NOMADVRLib/atoms.hpp
+++ b/NOMADVRLib/atoms.hpp
@@ -16,6 +16,8 @@ extern const char * TMPDIR;
 int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float ***pos);
 int readAtomsCube(const char *const file, int **numatoms, int *timesteps, float ***pos);
 int readAtomsJson (const char *const file, int **numatoms, int *timesteps, float ***pos, float abc[3][3],  std::vector<float>** clonedAtoms, const char *const token=0);
+int readAtomsAnalyticsJson(const char *const f, int **numatoms, int *timesteps, float ***pos, float abc[3][3],
+	std::vector<float>** clonedAtoms);
 int readAtomsJsonURL (const char *const f, int **numatoms, int *timesteps, float ***pos, float abc[3][3],  std::vector<float>** clonedAtoms, const char *const token=0);
 #if defined(WIN32) || defined(CAVE)
 int readAtomsJsonURLwget (const char *const f, int **numatoms, int *timesteps, float ***pos, float abc[3][3],  std::vector<float>** clonedAtoms, const char *const token=0);
@@ -28,6 +30,7 @@ const float MISSINGB=1.f;
 extern const char * readAtomsXYZErrors[];
 extern const char * readAtomsCubeErrors[];
 extern const char * readAtomsJsonErrors[];
+extern const char * readAtomsAnalyticsJsonErrors[];
 
 float atomRadius (int i);
 int findAtom(const char *const s);
@@ -35,6 +38,10 @@ int findAtom(const char *const s);
 //internal functions
 void discardline (FILE *F);
 void Clone (float tmppos[3], float k, std::vector<float>* clonedAtoms);
+bool CloneSpatialAtoms (float tmppos[3], float k, std::vector<float>* clonedAtoms);
 void TransformAtoms(std::vector<float>* clonedAtoms, const float abc[3][3]);
 
+extern bool inv_abc_init;
+extern float inv_abc[3][3];
+
 #endif //__ATOMS_H
diff --git a/OpenVR/TimestepData/hellovr_opengl_main.cpp b/OpenVR/TimestepData/hellovr_opengl_main.cpp
index 38cba011875ceced4c37a24671e9568915429426..2061fd9bd86bbdb56b55e31bbe9ba98cd316f3d7 100644
--- a/OpenVR/TimestepData/hellovr_opengl_main.cpp
+++ b/OpenVR/TimestepData/hellovr_opengl_main.cpp
@@ -17,7 +17,7 @@
 #include <stdio.h>
 #include <string>
 #include <cstdlib>
-
+#include <algorithm>
 
 #include <winsock2.h>
 
@@ -955,7 +955,7 @@ bool CMainApplication::HandleInput()
 				buttonPressed[1][unDevice] = true;
 				if (firstdevice == -1)
 					firstdevice = unDevice;
-				else if(seconddevice==-1)
+				else if(unDevice !=firstdevice && seconddevice==-1)
 					seconddevice=unDevice;
 
 				if (firstdevice==unDevice)
@@ -974,7 +974,7 @@ bool CMainApplication::HandleInput()
 				buttonPressed[0][unDevice] = true;
 				if (firstdevice == -1)
 					firstdevice = unDevice;
-				else if(seconddevice==-1)
+				else if(unDevice !=firstdevice && seconddevice==-1)
 					seconddevice=unDevice;
 
 				if (unDevice == firstdevice) {
@@ -993,6 +993,8 @@ bool CMainApplication::HandleInput()
 			{
 				if (firstdevice == -1)
 					firstdevice = unDevice;
+				else if (unDevice !=firstdevice && seconddevice==-1)
+					seconddevice=unDevice;
 				if (unDevice == firstdevice) {
 					Matrix4 tmp = m_mat4HMDPose;
 					UserPosition += tmp.invert()*Vector3(0, 0, speed);
@@ -1087,23 +1089,30 @@ void CMainApplication::HapticFeedback(){
 //-----------------------------------------------------------------------------
 void CMainApplication::HapticFeedback(int device)
 {
+	if (!numAtoms)
+		return;
 	if (device!=-1) {
 		vr::VRControllerState_t cs;
 		vr::TrackedDevicePose_t dp;
 		m_pHMD->GetControllerStateWithPose( vr::TrackingUniverseStanding, device, &cs, &dp );
 		if (dp.bPoseIsValid) {
+			int mycurrentset;
+			if (fixedAtoms)
+				mycurrentset=0;
+			else
+				mycurrentset=currentset;
 			vr::HmdMatrix34_t mat=dp.mDeviceToAbsoluteTracking;
 			Vector3 controllerPos(mat.m[0][3], mat.m[1][3],mat.m[2][3]);
 			int atomsInTimestep;
-			if (currentset==0)
+			if (mycurrentset==0)
 				atomsInTimestep=numAtoms[0];
 			else
-				atomsInTimestep=numAtoms[currentset]-numAtoms[currentset-1];
+				atomsInTimestep=numAtoms[mycurrentset]-numAtoms[mycurrentset-1];
 			for (int i=0;i<atomsInTimestep;i++) {
-				float atomr=atomRadius(atoms[currentset][i*4+3]);
+				float atomr=atomRadius(static_cast<int>(atoms[mycurrentset][i*4+3]));
 
 				//Vector3 posatom(atoms[currentset][i*4+0], atoms[currentset][i*4+1], atoms[currentset][i*4+2]);
-				Vector3 posatom(atoms[currentset][i*4+0], atoms[currentset][i*4+2], atoms[currentset][i*4+1]); //y/z flipped
+				Vector3 posatom(atoms[mycurrentset][i*4+0], atoms[mycurrentset][i*4+2], atoms[mycurrentset][i*4+1]); //y/z flipped
 				int p[3];
 				for (p[0]=0;p[0]<std::max(1,repetitions[0]);(p[0])++)
 					for (p[1]=0;p[1]<std::max(1,repetitions[1]);(p[1])++)
@@ -1123,11 +1132,11 @@ void CMainApplication::HapticFeedback(int device)
 						}
 			}
 			//now cloned atoms
-			if (currentset==0 && clonedAtoms) {
+			if (mycurrentset==0 && clonedAtoms) {
 				Vector3 up(-UserPosition.x, -UserPosition.y, UserPosition.z);
 				for (int i=0;i<numClonedAtoms;i++) {
-					float atomr=atomRadius(clonedAtoms[currentset][i*4+3]);
-					Vector3 posatom(clonedAtoms[currentset][i*4+0], clonedAtoms[currentset][i*4+2], clonedAtoms[currentset][i*4+1]);
+					float atomr=atomRadius(static_cast<int>(clonedAtoms[mycurrentset][i*4+3]));
+					Vector3 posatom(clonedAtoms[mycurrentset][i*4+0], clonedAtoms[mycurrentset][i*4+2], clonedAtoms[mycurrentset][i*4+1]);
 					Vector3 pos=posatom-up;
 					pos.z=-pos.z;
 					float l=(pos - controllerPos).length();
@@ -1601,7 +1610,7 @@ void CMainApplication::SetupIsosurfaces()
 			//matFinal.translate(translations[p%ISOS][0]+cubetrans[0], translations[p%ISOS][1]+cubetrans[1], translations[p%ISOS][2]+cubetrans[2]);
 			Matrix4 matcubetrans, mvs;
 			if (voxelSize[0]!=-1) {
-			mvs.scale(1.0 / (double)voxelSize[0], 1.0 / (double)voxelSize[1], 1.0 / (double)voxelSize[2]);
+			mvs.scale(1.0f / (float)voxelSize[0], 1.0f / (float)voxelSize[1], 1.0f / (float)voxelSize[2]);
 			matcubetrans.translate(cubetrans[0], cubetrans[1], cubetrans[2]); //angstrom
 			//if abc, in abc coordinates
 			/*Matrix4 abcm (abc[0][0], abc[1][0], abc[2][0], 0,
@@ -2958,7 +2967,10 @@ int main(int argc, char *argv[])
 			MessageBoxA(0, readAtomsXYZErrors[-r-100], "XYZ file reading error", 0);
 		else if (-300<r)
 			MessageBoxA(0, readAtomsCubeErrors[-r-200], "Cube file reading error", 0);
-		else MessageBoxA(0, readAtomsJsonErrors[-r-300], "Json reading error", 0);
+		else if (-400<r) 
+			MessageBoxA(0, readAtomsJsonErrors[-r-300], "Encyclopedia Json reading error", 0);
+		else
+			MessageBoxA(0, readAtomsAnalyticsJsonErrors[-r-400], "Analytics Json reading error", 0);
 		return -100+r;
 	}