diff --git a/vtm-web-app/src/org/oscim/web/client/GwtLauncher.java b/vtm-web-app/src/org/oscim/web/client/GwtLauncher.java
index 12a427ec..27a6235b 100644
--- a/vtm-web-app/src/org/oscim/web/client/GwtLauncher.java
+++ b/vtm-web-app/src/org/oscim/web/client/GwtLauncher.java
@@ -17,6 +17,7 @@
 package org.oscim.web.client;
 
 import org.oscim.core.Tile;
+import org.oscim.gdx.client.MapConfig;
 
 import com.badlogic.gdx.ApplicationListener;
 import com.badlogic.gdx.backends.gwt.GwtApplication;
diff --git a/vtm-web-app/src/org/oscim/web/client/GwtMap.java b/vtm-web-app/src/org/oscim/web/client/GwtMap.java
index 75c5a8a8..9147d534 100644
--- a/vtm-web-app/src/org/oscim/web/client/GwtMap.java
+++ b/vtm-web-app/src/org/oscim/web/client/GwtMap.java
@@ -16,8 +16,6 @@
  */
 package org.oscim.web.client;
 
-import java.util.HashMap;
-
 import org.oscim.backend.CanvasAdapter;
 import org.oscim.backend.GL20;
 import org.oscim.backend.GLAdapter;
@@ -25,7 +23,8 @@ import org.oscim.core.MapPosition;
 import org.oscim.gdx.GdxAssets;
 import org.oscim.gdx.GdxMap;
 import org.oscim.gdx.client.GwtGdxGraphics;
-import org.oscim.gdx.client.UrlUpdater;
+import org.oscim.gdx.client.MapConfig;
+import org.oscim.gdx.client.MapUrl;
 import org.oscim.layers.tile.bitmap.BitmapTileLayer;
 import org.oscim.layers.tile.s3db.S3DBLayer;
 import org.oscim.layers.tile.vector.BuildingLayer;
@@ -43,7 +42,6 @@ import org.slf4j.LoggerFactory;
 
 import com.badlogic.gdx.Gdx;
 import com.badlogic.gdx.backends.gwt.GwtApplication;
-import com.google.gwt.user.client.Window;
 
 class GwtMap extends GdxMap {
 	static final Logger log = LoggerFactory.getLogger(GwtMap.class);
@@ -73,72 +71,22 @@ class GwtMap extends GdxMap {
 
 		super.create();
 
-		double lat = c.getLatitude();
-		double lon = c.getLongitude();
-		int zoom = c.getZoom();
-
-		float tilt = 0;
-		float rotation = 0;
-		String themeName = null;
-		String mapName = null;
-
-		final HashMap<String, String> params = new HashMap<String, String>();
-		String addOpts = "";
-		if (Window.Location.getHash() != null) {
-			String hash = Window.Location.getHash();
-			hash = hash.substring(1);
-			String[] urlParams = null;
-			urlParams = hash.split("&");
-			if (urlParams.length == 1)
-				urlParams = hash.split(",");
-
-			for (String p : urlParams) {
-				try {
-					if (p.startsWith("lat="))
-						lat = Double.parseDouble(p.substring(4));
-					else if (p.startsWith("lon="))
-						lon = Double.parseDouble(p.substring(4));
-					else if (p.startsWith("scale="))
-						zoom = Integer.parseInt(p.substring(6));
-					else if (p.startsWith("rot="))
-						rotation = Float.parseFloat(p.substring(4));
-					else if (p.startsWith("tilt="))
-						tilt = Float.parseFloat(p.substring(5));
-					else if (p.startsWith("theme="))
-						themeName = p.substring(6);
-					else if (p.startsWith("map="))
-						mapName = p.substring(4);
-					else {
-						String[] opt = p.split("=");
-						if (opt.length > 1)
-							params.put(opt[0], opt[1]);
-						else
-							params.put(opt[0], null);
-
-						addOpts += p + "&";
-
-					}
-				} catch (NumberFormatException e) {
-
-				}
-			}
-		}
-
-		String addParam = (themeName == null ? "" : ("theme=" + themeName + "&"))
-		        + (mapName == null ? "" : ("map=" + mapName + "&"))
-		        + addOpts;
-
 		MapPosition p = new MapPosition();
-		p.setZoomLevel(zoom);
-		p.setPosition(lat, lon);
-		p.bearing = rotation;
-		p.tilt = tilt;
+		p.setZoomLevel(c.getZoom());
+		p.setPosition(c.getLatitude(), c.getLongitude());
+
+		MapUrl mapUrl = new MapUrl(mMap);
+		mapUrl.parseUrl(p);
+		mapUrl.scheduleRepeating(5000);
 
 		mMap.setMapPosition(p);
 
+		String mapName = mapUrl.getParam("map");
+		String themeName = mapUrl.getParam("theme");
+
 		VectorTileLayer l = null;
 
-		if (c.getBackgroundLayer() != null || mapName != null) {
+		if (mapName != null) {
 			BitmapTileSource ts;
 
 			if ("toner".equals(mapName))
@@ -156,9 +104,7 @@ class GwtMap extends GdxMap {
 
 			mMap.setBackgroundMap(new BitmapTileLayer(mMap, ts));
 		} else {
-			String url = c.getTileUrl();
-
-			TileSource ts = new OSciMap4TileSource(url);
+			TileSource ts = new OSciMap4TileSource();
 			l = mMap.setBaseMap(ts);
 
 			if (themeName == null) {
@@ -175,61 +121,24 @@ class GwtMap extends GdxMap {
 			}
 		}
 
-		if (params.containsKey("s3db")) {
+		boolean s3db = mapUrl.params.containsKey("s3db");
+		if (s3db) {
 			TileSource ts = new OSciMap4TileSource("http://opensciencemap.org/tiles/s3db");
 			mMap.layers().add(new S3DBLayer(mMap, ts));
 		}
-
 		if (l != null) {
-			if (!params.containsKey("nobuildings") && !params.containsKey("s3db"))
+			boolean nolabels = mapUrl.params.containsKey("nolabels");
+			boolean nobuildings = mapUrl.params.containsKey("nobuildings");
+
+			if (!nobuildings && !s3db)
 				mMap.layers().add(new BuildingLayer(mMap, l));
 
-			if (!params.containsKey("nolabel"))
+			if (!nolabels)
 				mMap.layers().add(new LabelLayer(mMap, l));
 		}
 
 		mSearchBox = new SearchBox(mMap);
 
-		//		// update URL hash to current position, every 5 seconds
-		//		Timer timer = new Timer() {
-		//			private int curLon, curLat, curZoom, curTilt, curRot;
-		//			private MapPosition pos = new MapPosition();
-		//
-		//			public void run() {
-		//				mMap.viewport().getMapPosition(pos);
-		//				int lat = (int) (MercatorProjection.toLatitude(pos.y) * 1000);
-		//				int lon = (int) (MercatorProjection.toLongitude(pos.x) * 1000);
-		//				int rot = (int) (pos.bearing);
-		//				rot = (int) (pos.bearing) % 360;
-		//				//rot = rot < 0 ? -rot : rot;
-		//
-		//				if (curZoom != pos.zoomLevel || curLat != lat || curLon != lon
-		//				        || curTilt != rot || curRot != (int) (pos.bearing)) {
-		//
-		//					curLat = lat;
-		//					curLon = lon;
-		//					curZoom = pos.zoomLevel;
-		//					curTilt = (int) pos.tilt;
-		//					curRot = rot;
-		//
-		//					String newURL = Window.Location
-		//					    .createUrlBuilder()
-		//					    .setHash(addParam
-		//					            + "scale=" + pos.zoomLevel
-		//					            + "&rot=" + curRot
-		//					            + "&tilt=" + curTilt
-		//					            + "&lat=" + (curLat / 1000f)
-		//					            + "&lon=" + (curLon / 1000f))
-		//					    .buildString();
-		//					Window.Location.replace(newURL);
-		//				}
-		//			}
-		//		};
-		//		timer.scheduleRepeating(5000);
-
-		UrlUpdater urlUpdater = new UrlUpdater(mMap);
-		urlUpdater.setParams(addParam);
-		urlUpdater.scheduleRepeating(5000);
 	}
 
 	@Override
diff --git a/vtm-web-js/src/org/oscim/web/client/GwtLauncher.java b/vtm-web-js/src/org/oscim/web/client/GwtLauncher.java
index 9ec9647f..3be62325 100644
--- a/vtm-web-js/src/org/oscim/web/client/GwtLauncher.java
+++ b/vtm-web-js/src/org/oscim/web/client/GwtLauncher.java
@@ -17,6 +17,7 @@
 package org.oscim.web.client;
 
 import org.oscim.core.Tile;
+import org.oscim.gdx.client.MapConfig;
 import org.timepedia.exporter.client.ExporterUtil;
 
 import com.badlogic.gdx.ApplicationListener;
diff --git a/vtm-web-js/src/org/oscim/web/client/GwtMap.java b/vtm-web-js/src/org/oscim/web/client/GwtMap.java
index 4c43ac42..464382e6 100644
--- a/vtm-web-js/src/org/oscim/web/client/GwtMap.java
+++ b/vtm-web-js/src/org/oscim/web/client/GwtMap.java
@@ -23,7 +23,8 @@ import org.oscim.core.MapPosition;
 import org.oscim.gdx.GdxAssets;
 import org.oscim.gdx.GdxMap;
 import org.oscim.gdx.client.GwtGdxGraphics;
-import org.oscim.gdx.client.UrlUpdater;
+import org.oscim.gdx.client.MapConfig;
+import org.oscim.gdx.client.MapUrl;
 import org.oscim.renderer.MapRenderer;
 import org.oscim.web.js.JsMap;
 import org.slf4j.Logger;
@@ -31,7 +32,6 @@ import org.slf4j.LoggerFactory;
 
 import com.badlogic.gdx.Gdx;
 import com.badlogic.gdx.backends.gwt.GwtApplication;
-import com.google.gwt.user.client.Window;
 
 public class GwtMap extends GdxMap {
 	static final Logger log = LoggerFactory.getLogger(GwtMap.class);
@@ -48,10 +48,6 @@ public class GwtMap extends GdxMap {
 
 		JsMap.init(mMap);
 
-		// stroke text takes about 70% cpu time in firefox:
-		// https://bug568526.bugzilla.mozilla.org/attachment.cgi?id=447932
-		// <- circle/stroke test 800ms firefox, 80ms chromium..
-		// TODO use texture atlas to avoid drawing text-textures
 		if (GwtApplication.agentInfo().isLinux() &&
 		        GwtApplication.agentInfo().isFirefox())
 			GwtGdxGraphics.NO_STROKE_TEXT = true;
@@ -59,45 +55,13 @@ public class GwtMap extends GdxMap {
 		MapConfig c = MapConfig.get();
 		super.create();
 
-		double lat = c.getLatitude();
-		double lon = c.getLongitude();
-		int zoom = c.getZoom();
-
-		float tilt = 0;
-		float rotation = 0;
-
-		if (Window.Location.getHash() != null) {
-			String hash = Window.Location.getHash();
-
-			hash = hash.substring(1);
-			String[] pairs = hash.split(",");
-
-			for (String p : pairs) {
-				try {
-					if (p.startsWith("lat="))
-						lat = Double.parseDouble(p.substring(4));
-					else if (p.startsWith("lon="))
-						lon = Double.parseDouble(p.substring(4));
-					else if (p.startsWith("scale="))
-						zoom = Integer.parseInt(p.substring(6));
-					else if (p.startsWith("rot="))
-						rotation = Float.parseFloat(p.substring(4));
-					else if (p.startsWith("tilt="))
-						tilt = Float.parseFloat(p.substring(5));
-				} catch (NumberFormatException e) {
-
-				}
-			}
-		}
 		MapPosition p = new MapPosition();
-		p.setZoomLevel(zoom);
-		p.setPosition(lat, lon);
-		p.bearing = rotation;
-		p.tilt = tilt;
-		mMap.setMapPosition(p);
+		p.setZoomLevel(c.getZoom());
+		p.setPosition(c.getLatitude(), c.getLongitude());
 
-		UrlUpdater urlUpdater = new UrlUpdater(mMap);
-		urlUpdater.scheduleRepeating(4000);
+		MapUrl mapUrl = new MapUrl(mMap);
+		mapUrl.parseUrl(p);
+		mapUrl.scheduleRepeating(5000);
 	}
 
 	private final native void createLayersN()/*-{
diff --git a/vtm-web-js/src/org/oscim/web/client/MapConfig.java b/vtm-web-js/src/org/oscim/web/client/MapConfig.java
deleted file mode 100644
index 131c9ca0..00000000
--- a/vtm-web-js/src/org/oscim/web/client/MapConfig.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2013 Hannes Janetzek
- *
- * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package org.oscim.web.client;
-
-import com.google.gwt.core.client.JavaScriptObject;
-
-class MapConfig extends JavaScriptObject {
-	protected MapConfig() {
-	}
-
-	public static native MapConfig get()/*-{
-		return $wnd.mapconfig;
-	}-*/;
-
-	public final native double getLatitude() /*-{
-		return this.latitude;
-	}-*/;
-
-	public final native double getLongitude() /*-{
-		return this.longitude;
-	}-*/;
-
-	public final native int getZoom() /*-{
-		return this.zoom;
-	}-*/;
-
-	public final native String getTileSource() /*-{
-		return this.tilesource;
-	}-*/;
-
-	public final native String getTileUrl() /*-{
-		return this.tileurl;
-	}-*/;
-
-	public final native String getBackgroundLayer() /*-{
-		return this.background;
-	}-*/;
-
-	public final native int getTileSize() /*-{
-		return this.tileSize || 256;
-	}-*/;
-
-}
diff --git a/vtm-web-app/src/org/oscim/web/client/MapConfig.java b/vtm-web/src/org/oscim/gdx/client/MapConfig.java
similarity index 80%
rename from vtm-web-app/src/org/oscim/web/client/MapConfig.java
rename to vtm-web/src/org/oscim/gdx/client/MapConfig.java
index 131c9ca0..2f006137 100644
--- a/vtm-web-app/src/org/oscim/web/client/MapConfig.java
+++ b/vtm-web/src/org/oscim/gdx/client/MapConfig.java
@@ -14,11 +14,11 @@
  * You should have received a copy of the GNU Lesser General Public License along with
  * this program. If not, see <http://www.gnu.org/licenses/>.
  */
-package org.oscim.web.client;
+package org.oscim.gdx.client;
 
 import com.google.gwt.core.client.JavaScriptObject;
 
-class MapConfig extends JavaScriptObject {
+public class MapConfig extends JavaScriptObject {
 	protected MapConfig() {
 	}
 
@@ -27,29 +27,21 @@ class MapConfig extends JavaScriptObject {
 	}-*/;
 
 	public final native double getLatitude() /*-{
-		return this.latitude;
+		return this.latitude || 0;
 	}-*/;
 
 	public final native double getLongitude() /*-{
-		return this.longitude;
+		return this.longitude || 0;
 	}-*/;
 
 	public final native int getZoom() /*-{
-		return this.zoom;
+		return this.zoom || 2;
 	}-*/;
 
 	public final native String getTileSource() /*-{
 		return this.tilesource;
 	}-*/;
 
-	public final native String getTileUrl() /*-{
-		return this.tileurl;
-	}-*/;
-
-	public final native String getBackgroundLayer() /*-{
-		return this.background;
-	}-*/;
-
 	public final native int getTileSize() /*-{
 		return this.tileSize || 256;
 	}-*/;
diff --git a/vtm-web/src/org/oscim/gdx/client/MapUrl.java b/vtm-web/src/org/oscim/gdx/client/MapUrl.java
new file mode 100644
index 00000000..97b96773
--- /dev/null
+++ b/vtm-web/src/org/oscim/gdx/client/MapUrl.java
@@ -0,0 +1,120 @@
+package org.oscim.gdx.client;
+
+import java.util.HashMap;
+
+import org.oscim.core.MapPosition;
+import org.oscim.core.MercatorProjection;
+import org.oscim.map.Map;
+
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.Window;
+
+public class MapUrl extends Timer {
+	private int curLon, curLat, curZoom, curTilt, curRot;
+	private MapPosition pos = new MapPosition();
+	private final Map mMap;
+	private String mParams = "";
+
+	public MapUrl(Map map) {
+		mMap = map;
+	}
+
+	public String getParam(String name) {
+		return params.get(name);
+	}
+
+	public final HashMap<String, String> params = new HashMap<String, String>();
+
+	public void parseUrl(MapPosition pos) {
+
+		//String addOpts = "";
+		if (Window.Location.getHash() == null)
+			return;
+
+		String hash = Window.Location.getHash();
+		hash = hash.substring(1);
+		String[] urlParams = null;
+		urlParams = hash.split("&");
+		if (urlParams.length == 1)
+			urlParams = hash.split(",");
+		double lat = pos.getLatitude(), lon = pos.getLongitude();
+		float rotation = pos.bearing;
+		float tilt = pos.tilt;
+
+		//String themeName = "";
+		//String mapName = "";
+
+		int zoom = pos.zoomLevel;
+
+		for (String p : urlParams) {
+			try {
+				if (p.startsWith("lat="))
+					lat = Double.parseDouble(p.substring(4));
+
+				else if (p.startsWith("lon="))
+					lon = Double.parseDouble(p.substring(4));
+				else if (p.startsWith("scale="))
+					zoom = Integer.parseInt(p.substring(6));
+				else if (p.startsWith("rot="))
+					rotation = Float.parseFloat(p.substring(4));
+				else if (p.startsWith("tilt="))
+					tilt = Float.parseFloat(p.substring(5));
+				//	else if (p.startsWith("theme="))
+				//		themeName = p.substring(6);
+				//	else if (p.startsWith("map="))
+				//		mapName = p.substring(4);
+				else {
+					String[] opt = p.split("=");
+					if (opt.length > 1)
+						params.put(opt[0], opt[1]);
+					else
+						params.put(opt[0], null);
+
+					mParams += p + "&";
+
+				}
+			} catch (NumberFormatException e) {
+
+			}
+		}
+		pos.setPosition(lat, lon);
+		pos.setZoomLevel(zoom);
+		pos.set(MercatorProjection.longitudeToX(lon),
+		        MercatorProjection.latitudeToY(lat),
+		        1 << zoom,
+		        rotation,
+		        tilt);
+
+	}
+
+	@Override
+	public void run() {
+		mMap.viewport().getMapPosition(pos);
+		int lat = (int) (MercatorProjection.toLatitude(pos.y) * 1000);
+		int lon = (int) (MercatorProjection.toLongitude(pos.x) * 1000);
+		int rot = (int) (pos.bearing);
+		rot = (int) (pos.bearing) % 360;
+		//rot = rot < 0 ? -rot : rot;
+
+		if (curZoom != pos.zoomLevel || curLat != lat || curLon != lon
+		        || curTilt != rot || curRot != (int) (pos.bearing)) {
+
+			curLat = lat;
+			curLon = lon;
+			curZoom = pos.zoomLevel;
+			curTilt = (int) pos.tilt;
+			curRot = rot;
+
+			String newURL = Window.Location
+			    .createUrlBuilder()
+			    .setHash(mParams
+			            + "scale=" + pos.zoomLevel
+			            + "&rot=" + curRot
+			            + "&tilt=" + curTilt
+			            + "&lat=" + (curLat / 1000f)
+			            + "&lon=" + (curLon / 1000f))
+			    .buildString();
+			Window.Location.replace(newURL);
+		}
+	}
+}
diff --git a/vtm-web/src/org/oscim/gdx/client/UrlUpdater.java b/vtm-web/src/org/oscim/gdx/client/UrlUpdater.java
deleted file mode 100644
index 21343458..00000000
--- a/vtm-web/src/org/oscim/gdx/client/UrlUpdater.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.oscim.gdx.client;
-
-import org.oscim.core.MapPosition;
-import org.oscim.core.MercatorProjection;
-import org.oscim.map.Map;
-
-import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.Window;
-
-public class UrlUpdater extends Timer {
-	private int curLon, curLat, curZoom, curTilt, curRot;
-	private MapPosition pos = new MapPosition();
-	private final Map mMap;
-	private String mParams = "";
-
-	public UrlUpdater(Map map) {
-		mMap = map;
-	}
-
-	public void setParams(String params) {
-		mParams = params;
-	}
-
-	@Override
-	public void run() {
-		mMap.viewport().getMapPosition(pos);
-		int lat = (int) (MercatorProjection.toLatitude(pos.y) * 1000);
-		int lon = (int) (MercatorProjection.toLongitude(pos.x) * 1000);
-		int rot = (int) (pos.bearing);
-		rot = (int) (pos.bearing) % 360;
-		//rot = rot < 0 ? -rot : rot;
-
-		if (curZoom != pos.zoomLevel || curLat != lat || curLon != lon
-		        || curTilt != rot || curRot != (int) (pos.bearing)) {
-
-			curLat = lat;
-			curLon = lon;
-			curZoom = pos.zoomLevel;
-			curTilt = (int) pos.tilt;
-			curRot = rot;
-
-			String newURL = Window.Location
-			    .createUrlBuilder()
-			    .setHash(mParams + "scale=" + pos.zoomLevel + ",rot=" + curRot
-			            + ",tilt=" + curTilt + ",lat=" + (curLat / 1000f)
-			            + ",lon=" + (curLon / 1000f))
-			    .buildString();
-			Window.Location.replace(newURL);
-		}
-	}
-}