diff --git a/NOMADVRLib/ConfigFile.cpp b/NOMADVRLib/ConfigFile.cpp
index 365a442dfd7fb7e3466eb6f1f6acca4029180ca8..c776ea4b83d1f578317aaf136dc9a078e1425409 100644
--- a/NOMADVRLib/ConfigFile.cpp
+++ b/NOMADVRLib/ConfigFile.cpp
@@ -86,6 +86,8 @@ int secret;
 const char * server;
 int port;
 
+bool resetTimestepOnReload;
+
 const char * loadConfigFileErrors[] =
 {
 	"All Ok",//0
@@ -115,12 +117,34 @@ const char * loadConfigFileErrors[] =
 	"Error loading analytics json file, add 400 to see the error",//<-400
 };
 
+void cleanMarkers()
+{
+	for (int i=0;i<TIMESTEPS;i++)
+		delete markers[i];
+	delete[] markers;
+	markers=nullptr;
+}
+
+void cleanMarkerColours() {
+	for (int i=0;i<TIMESTEPS;i++)
+		delete markercolours[i];
+	delete[] markercolours;
+	markercolours=nullptr;
+}
+
 void updateTIMESTEPS (int timesteps)
 { 
-if (TIMESTEPS==0)
+if (TIMESTEPS==0) {
 	TIMESTEPS=timesteps;
-else
-	TIMESTEPS=std::min(TIMESTEPS, timesteps);
+} else {
+	if (TIMESTEPS!=timesteps) {
+		if (markers)
+			cleanMarkers();
+		if (markercolours)
+			cleanMarkerColours();
+		TIMESTEPS=std::min(TIMESTEPS, timesteps);
+	}
+}
 }
 
 int readString(FILE *f, char *s)
@@ -155,6 +179,9 @@ int readString(FILE *f, char *s)
 
 void fixFile(char * file)
 {
+if (!strncmp (file, "http://", 7) || !strncmp(file, "https://", 8))
+	return;
+
 #ifdef WIN32
 	const char c='\\';
 #else
@@ -190,13 +217,11 @@ void cleanConfig()
 	SCREENSHOT=nullptr;
 
 	if (markers) {
+		cleanMarkers();
 		for (int i=0;i<TIMESTEPS;i++) {
-			delete[] markers[i];
 			delete[] markercolours[i];
 		}
-		delete[] markers;
 		delete[] markercolours;
-		markers=nullptr;
 		markercolours=nullptr;
 	}
 	for (int i=0;i<info.size();i++) {
@@ -301,8 +326,10 @@ void initState()
 	secret=0;
 	server=nullptr;
 	port=-1;
+	resetTimestepOnReload=true;
 }
 
+
 int loadConfigFile(const char * f)
 {
 	//default values
@@ -335,10 +362,7 @@ int loadConfigFile(const char * f)
 		if (!strcmp(s, "timesteps")) {
 			int timesteps;
 			r = fscanf(F, "%d", &timesteps);
-			if (TIMESTEPS==0)
-				TIMESTEPS=timesteps;
-			else
-				TIMESTEPS=std::min(TIMESTEPS, timesteps);
+			updateTIMESTEPS(timesteps);
 		}
 		else if (!strcmp(s, "isos")) {
 			if (ISOS!=0) 
@@ -572,6 +596,8 @@ int loadConfigFile(const char * f)
 				fclose(F);
 				return -17;
 			}
+			if (markers) 
+				cleanMarkers();
 			markers=new float* [TIMESTEPS];
 			for (int i=0;i<TIMESTEPS;i++) {
 				markers[i]=new float[3];
@@ -755,6 +781,8 @@ int loadConfigFile(const char * f)
 #endif
 		} else if (!strcmp (s, "\x0d")) { //discard windows newline (problem in Sebastian Kokott's phone (?!)
 			continue;
+		} else if (!strcmp (s, "disablereloadreset")) {
+			resetTimestepOnReload=false;
 		} else if (!strcmp (s, "server")) { //multiuser support
 			int r;
 			if (server)
diff --git a/NOMADVRLib/ConfigFile.h b/NOMADVRLib/ConfigFile.h
index bf252fe11b9eda10e8d381b383da2aff9536d138..8c7fe0211cfe7e88a91a20a2647061b434c5da72 100644
--- a/NOMADVRLib/ConfigFile.h
+++ b/NOMADVRLib/ConfigFile.h
@@ -88,6 +88,8 @@ extern int secret;
 extern const char * server;
 extern int port;
 
+extern bool resetTimestepOnReload;
+
 struct information {
 	float pos[3];
 	float size;
diff --git a/NOMADVRLib/Grid.cpp b/NOMADVRLib/Grid.cpp
index cc217c4723c4e080f0b9c41271ab5657733d605b..0437b2bfc5bdecfe7158658075e76d08cd8443fb 100644
--- a/NOMADVRLib/Grid.cpp
+++ b/NOMADVRLib/Grid.cpp
@@ -38,7 +38,7 @@ grid::~grid()
 void grid::coordinates(const float pos[3], int c[3])
 {
 	for (int i=0;i<3;i++) {
-		c[i]=floor((pos[i]-m[i])/dif[i]*dims);
+		c[i]=static_cast<int>(floor((pos[i]-m[i])/dif[i]*dims));
 		if (c[i]>=dims)
 			c[i]=dims-1;
 		else if (c[i]<0)
@@ -52,7 +52,7 @@ void grid::add (float *p) //compatible with the atoms xyzr
 	coordinates (p, pos);
 	
 	content[pos[0]*dims*dims + pos[1]*dims+pos[2]].push_back(p);
-	float ar=atomRadius(p[3]);
+	float ar=atomRadius(static_cast<int>(p[3]));
 	if (ar>maxradius)
 		maxradius=ar;
 }
@@ -61,7 +61,7 @@ bool grid::compare (float *a, float *b)
 {
 	if (a<=b) //already returned when searching a beforehand
 		return false;
-	float sqd=atomRadius(a[3])+atomRadius(b[3]);
+	float sqd=atomRadius(static_cast<int>(a[3]))+atomRadius(static_cast<int>(b[3]));
 	sqd*=sqd;
 	float d=0;
 	for (int i=0;i<3;i++)
@@ -82,8 +82,8 @@ std::vector<float*> grid::find (float *p)
 	float mp[3];
 	float Mp[3];
 	for (int i=0;i<3;i++) {
-		mp[i]=p[i]-(atomRadius(p[3])+maxradius)/scale;
-		Mp[i]=p[i]+(atomRadius(p[3])+maxradius)/scale;
+		mp[i]=p[i]-(atomRadius(static_cast<int>(p[3]))+maxradius)/scale;
+		Mp[i]=p[i]+(atomRadius(static_cast<int>(p[3]))+maxradius)/scale;
 	}
 	coordinates(mp, mc);
 	coordinates(Mp, Mc);
diff --git a/NOMADVRLib/atoms.cpp b/NOMADVRLib/atoms.cpp
index 6876f4931d0ed45dd5f4ef35d3561cc490a1b3c4..679f588d476d7b49655886fcaf425188f7a0a8fc 100644
--- a/NOMADVRLib/atoms.cpp
+++ b/NOMADVRLib/atoms.cpp
@@ -227,7 +227,7 @@ int findAtom(const char *const s)
 	strcpy (x, s);
 	char *p=x+strlen(x);
 	p--;
-	while (*p > '0' && *p <= '9') {
+	while (*p >= '0' && *p <= '9') {
 		*p='\0';
 		*p--;
 		if (p==x)
@@ -260,7 +260,8 @@ const char * readAtomsXYZErrors[] = {
 	"Could not open file", //-1
 	"Error loading atom type and position line", //-2
 	"Atom type unknown", //-3
-	"Corrupt xyz file" //-4
+	"Corrupt xyz file", //-4
+	"Unable to download xyz file" //-5
 };
 
 void cleanAtoms (int **numatoms, int timesteps, float ***pos)
@@ -276,10 +277,27 @@ void cleanAtoms (int **numatoms, int timesteps, float ***pos)
 
 int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float ***pos) 
 {
+	const char *myfile=nullptr;
+	const char *webdownload="material.xyz";
+	//add http support
+	if (!strncmp(file, "http:", 5) || !strncmp(file, "https:", 6)) {
+		char cmd[2048];
+		int ret;
+		sprintf (cmd, "wget %s -O %s", file, webdownload);
+		ret=system(cmd);
+		if (ret!=0) {
+			*numatoms=nullptr;
+			*pos=nullptr;
+			return (-5);
+		}
+		myfile=webdownload;
+	} else {
+		myfile = file;
+	}
 	int mynumatoms;
 	std::vector<float*> mypos;
 	std::vector<int> mynum;
-	FILE *f=fopen (file, "r");
+	FILE *f=fopen (myfile, "r");
 	int r;
 	char s[100];
 	if (f==0) {
@@ -294,6 +312,7 @@ int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float *
 			blanklines++;
 			if (blanklines>3) {
 				eprintf("Corrupt xyz file %s. Error at %d atoms\n", file, mynumatoms);
+				fclose(f);
 				return -4;
 			}
 			continue; //there may be a blank line at the end of the file
@@ -311,8 +330,10 @@ int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float *
 			char line[512];
 			fgets(line, 512, f);
 			r=sscanf (line, "%s %f %f %f %f", s, mypos.back()+4*i+0, mypos.back()+4*i+1,mypos.back()+4*i+2, &unused);
-			if (r<4)
+			if (r<4) {
+				fclose (f);
 				return -2;
+			}
 			int a=findAtom(s);
 			if (a==-1) {
 				eprintf ("Read atoms xyz, atom type unknown: %s", s);
@@ -331,7 +352,7 @@ int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float *
 		(*numatoms)[i]=mynum[i];
 		//eprintf ("Getting atoms, numatoms=%d",(*numatoms)[i]);
 	}
-
+	fclose (f);
 	return 0;
 }
 
@@ -367,9 +388,10 @@ int readAtomsCube(const char *const file, int **numatoms, int *timesteps, float
 	discardline(f); //two comments
 	discardline(f);
 	r = fscanf(f, "%d %f %f %f", *numatoms, cubetrans + 0, cubetrans + 1, cubetrans + 2);
-	if (r < 4)
+	if (r < 4) {
+		fclose (f);
 		return -2;
-
+	}
 	//rgh FIXME. Is this always bohr?
 	for (int i=0;i<3;i++)
 		cubetrans[i]*= 0.52918f;
diff --git a/NOMADVRLib/atomsGL.cpp b/NOMADVRLib/atomsGL.cpp
index 6c20dee2a10f701476430cd60adc40bec2c7bfe0..6efdd99a083e211d03de16c04eb1021c0b7c3c89 100644
--- a/NOMADVRLib/atomsGL.cpp
+++ b/NOMADVRLib/atomsGL.cpp
@@ -227,6 +227,9 @@ void CleanAtoms (GLuint **AtomVAO /*[4]*/, GLuint **AtomVertBuffer /*[3]*/, GLui
 	if (!numAtoms)
 		return;
 
+	if (*AtomVAO==nullptr)
+		return;
+
 	glDeleteVertexArrays(4, *AtomVAO);
 	glDeleteBuffers(3, *AtomVertBuffer);
 	glDeleteBuffers(1, BondIndices);
@@ -764,6 +767,7 @@ GLenum SetupUnitCell(GLuint *UnitCellVAO, GLuint *UnitCellVertBuffer, GLuint *Un
 	if ((e = glGetError()) != GL_NO_ERROR)
 		eprintf( "opengl error %d, glBufferData index, l %d\n", e, __LINE__);
 	glBindVertexArray(0);
+	delete[] tmp;
 	return e;
 }
 
diff --git a/OpenVR/TimestepData/hellovr_opengl_main.cpp b/OpenVR/TimestepData/hellovr_opengl_main.cpp
index 2bc4e31e6e9184a8a8bb74637a55696f383561e6..c4b8831e13f2e508aad17cd845ddd907a422b7fc 100644
--- a/OpenVR/TimestepData/hellovr_opengl_main.cpp
+++ b/OpenVR/TimestepData/hellovr_opengl_main.cpp
@@ -351,6 +351,7 @@ private: // OpenGL bookkeeping
 	char *pixels, *pixels2; //for saving screenshots to disk
 	int framecounter;
 	bool savetodisk;
+	bool showInfoBox;
 
 	int selectedAtom;
 
@@ -370,7 +371,7 @@ private: // OpenGL bookkeeping
 	void UDP();
 	int UDPContr(SOCKET s, char c, int device);
 	int UDPHead(SOCKET s);
-	int sock, s;
+	SOCKET sock, s;
 	struct sockaddr_in serv_addr;
 	void Send(char c, int32_t value);
 	void Send(char c, bool value);
@@ -417,6 +418,10 @@ void dprintf( const char *fmt, ... )
 		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Warning", buffer, 0);
 }
 
+void message (char *buffer) 
+{
+MessageBoxA (0, buffer, "Warning", 0);
+}
 
 //pure windows, no sdl
 void eprintf( const char *fmt, ... )
@@ -439,7 +444,7 @@ void eprintf( const char *fmt, ... )
 	if ( g_bPrintf )
 		printf( "%s", buffer );
 
-	MessageBoxA(0, buffer, "Warning", 0);
+	std::thread(message, buffer).detach();
 }
 
 int CMainApplication::LoadConfigFile (const char *c)
@@ -689,7 +694,10 @@ void CMainApplication::connectTCP()
 				eprintf ("short read at socket\n");
 				return;
 			}
-			currentset=ntohl(tmp)%TIMESTEPS;
+			if (TIMESTEPS!=0)
+				currentset=ntohl(tmp)%TIMESTEPS;
+			else
+				currentset=ntohl(tmp);
 			break;
 		case 'i':
 			n=recv (sock, (char*)&tmp, sizeof(tmp), 0);
@@ -726,13 +734,6 @@ void CMainApplication::connectTCP()
 				std::unique_lock<std::mutex> lk(cleanupmutex);
 				cleanupcond.wait(lk);
 
-				//OpenGL commands only in main thread
-				//CleanScene();
-				//LoadConfigFile(myargv[currentConfig]);
-				//SetupScene();
-				//cleanupmutex.lock();
-				//cleanup=0;
-				//cleanupmutex.unlock();
 			}
 			break;
 		case 'X':
@@ -850,6 +851,8 @@ CMainApplication::CMainApplication(int argc, char *argv[])
 	, identifier(0)
 	, whenDrag (3)
 	, cleanup (0)
+	, showInfoBox (true)
+	, m_unInfoVAO (0)
 {
 	LoadConfigFile(argv[currentConfig]);
 	for (int j=0;j<3;j++)
@@ -1134,6 +1137,7 @@ void CMainApplication::Shutdown()
 	if (tcpconn) {
 		closesocket(sock);
 		sock=INVALID_SOCKET;
+		cleanupcond.notify_one(); //in case we are waiting for update
 		tcpconn->join();
 		delete tcpconn;
 		tcpconn=nullptr;
@@ -1357,7 +1361,11 @@ void CMainApplication::Send(char c, bool value)
 
 void CMainApplication::SendConfigFile()
 {
-	Send('n', currentConfig);
+	if (sock != INVALID_SOCKET) {
+		Send('n', currentConfig);
+	} else {//notify for local execution
+		cleanup=1;
+	}
 }
 
 void CMainApplication::SendTimestep()
@@ -1501,19 +1509,12 @@ bool CMainApplication::HandleInput()
 						if (currentConfig>=myargc)
 							currentConfig=1;
 						SendConfigFile();
-						//we will soon receive an order to reload the scene, so avoid double-reloading + race here
-						//CleanScene();
-						//LoadConfigFile(myargv[currentConfig]);
-						//SetupScene();
 					} else if (state.rAxis[0].y < -0.7 && state.rAxis[0].x > -0.4 && state.rAxis[0].x < 0.4) {
 						//prev config file
 						currentConfig--;
 						if (currentConfig<=0)
 							currentConfig=myargc-1;
 						SendConfigFile();
-						//CleanScene();
-						//LoadConfigFile(myargv[currentConfig]);
-						//SetupScene();
 					}
 				}
 			} else { //Drag and Drop
@@ -1552,8 +1553,12 @@ bool CMainApplication::HandleInput()
 				else if(unDevice !=firstdevice && seconddevice==-1)
 					seconddevice=unDevice;
 */
-				if (firstdevice==unDevice)
-					savetodisk = !savetodisk;
+				if (firstdevice==unDevice) {
+					if (menubutton==Record)
+						savetodisk = !savetodisk;
+					else if (menubutton==Infobox)
+						showInfoBox= !showInfoBox;
+				}
 				else {
 					showAtoms= !showAtoms;
 					SendShowAtoms();
@@ -1793,7 +1798,6 @@ void CMainApplication::RenderFrame()
 		cleanup=0;
 		cleanupmutex.unlock();
 		cleanupcond.notify_one();
-		//return;
 	}
 
 	int e;
@@ -2089,7 +2093,8 @@ void CMainApplication::CleanScene()
 		CleanMarker(&m_unMarkerVAO, &m_glMarkerVertBuffer);
 	}
 	//infocube
-	::CleanInfoCube(&m_unInfoVAO, &m_unInfoVertBuffer, &m_unInfoIndexBuffer);
+	if (m_unInfoVAO != 0)
+		::CleanInfoCube(&m_unInfoVAO, &m_unInfoVertBuffer, &m_unInfoIndexBuffer);
 	cleanConfig();
 	ISOS=0;
 }
@@ -2108,6 +2113,14 @@ void CMainApplication::SetupScene()
 	SetupInfoCube();
 	movementspeed/=scaling;
 	SetupInfoBoxTexture();
+	SetupCameras(); //near and far plane may have changed
+	if (resetTimestepOnReload) {
+		currentset=0;
+		currentiso=ISOS;
+		UserPosition=Vector3(-userpos[0], -userpos[1], -userpos[2]);
+		SendUserPos();
+		selectedAtom=-1;
+	}
 }
 
 void CMainApplication::SetupInfoCube()
@@ -2155,7 +2168,7 @@ Matrix4 i = matDeviceToTracking;
 Matrix4 trans;
 
 
-Vector3 iPos = (*pos)+Vector3(0,0.02,0); //raise glyph
+Vector3 iPos = (*pos)+Vector3(0.0f,0.02f,0.0f); //raise glyph
 
 int e;
 trans.scale(0.05).translate(iPos); //translate(0,0.1,0);
@@ -2178,31 +2191,31 @@ return true;
 void FillVerticesGlyph (float * const vert, const int i, const float u)
 {
 for (int j=0;j<4;j++) {
-			vert[i*9*4+j*9+2]=0; //z
-			vert[i*9*4+j*9+3]=1; //w
-			vert[i*9*4+j*9+4]=0; //nx
-			vert[i*9*4+j*9+5]=0; //ny
-			vert[i*9*4+j*9+6]=-1; //nz
-		}
-		vert [i*9*4+0*9+0]=i+0; //x
-		vert [i*9*4+0*9+1]=0; //y
+			vert[i*9*4+j*9+2]=0.0f; //z
+			vert[i*9*4+j*9+3]=1.0f; //w
+			vert[i*9*4+j*9+4]=0.0f; //nx
+			vert[i*9*4+j*9+5]=0.0f; //ny
+			vert[i*9*4+j*9+6]=-1.0f; //nz
+		}
+		vert [i*9*4+0*9+0]=static_cast<float>(i+0); //x
+		vert [i*9*4+0*9+1]=0.0f; //y
 		vert [i*9*4+0*9+7]=u; //u
-		vert [i*9*4+0*9+8]=1; //v
+		vert [i*9*4+0*9+8]=1.0f; //v
 
-		vert [i*9*4+1*9+0]=i+1; //x
-		vert [i*9*4+1*9+1]=0; //y
+		vert [i*9*4+1*9+0]=static_cast<float>(i+1); //x
+		vert [i*9*4+1*9+1]=0.0f; //y
 		vert [i*9*4+1*9+7]=u+1.0f/16.0f; //u
-		vert [i*9*4+1*9+8]=1; //v
+		vert [i*9*4+1*9+8]=1.0f; //v
 
-		vert [i*9*4+2*9+0]=i+0; //x
-		vert [i*9*4+2*9+1]=1; //y
+		vert [i*9*4+2*9+0]=static_cast<float>(i+0); //x
+		vert [i*9*4+2*9+1]=1.0f; //y
 		vert [i*9*4+2*9+7]=u; //u
-		vert [i*9*4+2*9+8]=0; //v
+		vert [i*9*4+2*9+8]=0.0f; //v
 
-		vert [i*9*4+3*9+0]=i+1; //x
-		vert [i*9*4+3*9+1]=1; //y
+		vert [i*9*4+3*9+0]=static_cast<float>(i+1); //x
+		vert [i*9*4+3*9+1]=1.0f; //y
 		vert [i*9*4+3*9+7]=u+1.0f/16.0f; //u
-		vert [i*9*4+3*9+8]=0; //v
+		vert [i*9*4+3*9+8]=0.0f; //v
 }
 
 short int *FillIndicesGlyph (int l)
@@ -2303,7 +2316,7 @@ void CMainApplication::RenderControllerGlyph (const vr::Hmd_Eye nEye, const int
 			SendDragDrop(pos);
 		}
 
-		if (selectedAtom==-1) { //isos
+		if (selectedAtom==-1 || numAtoms==nullptr || numAtoms[currentset]>=selectedAtom) { //isos
 			sprintf (string, "%d", currentiso+1);
 		} else {
 			pos-=Vector3(atoms[currentset][selectedAtom*4+0], atoms[currentset][selectedAtom*4+1], atoms[currentset][selectedAtom*4+2]);
@@ -3328,7 +3341,7 @@ void CMainApplication::RenderScene(vr::Hmd_Eye nEye)
 			glEnable(GL_BLEND);
 			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 		}
-		if (menubutton==Infobox && savetodisk)
+		if (showInfoBox)
 			RenderInfo(nEye);
 		RenderAtoms(nEye);
 		RenderUnitCell(nEye);
@@ -3954,17 +3967,11 @@ char * MainErrors [] = {
 int main(int argc, char *argv[])
 {
 	//https://stackoverflow.com/questions/8544090/detected-memory-leaks
-	/*_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
-		 _CrtSetBreakAlloc(639);
-	 _CrtSetBreakAlloc(783);
-	 _CrtSetBreakAlloc(676);
-	 _CrtSetBreakAlloc(675);
-	 _CrtSetBreakAlloc(639);
-	 _CrtSetBreakAlloc(125);
-	 _CrtSetBreakAlloc(118);
-	 _CrtSetBreakAlloc(117);
-	 _CrtSetBreakAlloc(94);
-	 _CrtSetBreakAlloc(93);*/
+	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );/*
+		 _CrtSetBreakAlloc(1969);
+	 _CrtSetBreakAlloc(1918);
+	 _CrtSetBreakAlloc(1226);
+	 */
 	TMPDIR=".\\";
 	//http://stackoverflow.com/questions/4991967/how-does-wsastartup-function-initiates-use-of-the-winsock-dll
 	WSADATA wsaData;
diff --git a/README.md b/README.md
index ca2e4fae8c887a6ed544991c26faedb610226a04..adb848541b7b46d34720ab921b0c9df1b32c21c7 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,13 @@ Subdirectories:
 		Transforms a json from encyclopedia or archive into an XYZ file	
 		Useful for the remote visualization infrastructure
 	
+	proxy: Back-end support for multiuser support in OpenVR NOMAD VR. 
+		NOMADVRproxy listens to connections and forwards user actions to rest of
+			users.
+			
+		MD-Driver/SimpleMove and MD-Driver/PeriodicTable: see MD-Driver/Readme
+			Support for atom drag-and-drop functionality in OpenVR NOMAD VR.
+	
 More platforms will be added in the future.
 
 The code is distributed under the Apache 2.0 License (See LICENSE file).