   resetLogContent()
 
   # A hash needs to be set, or we won't be able to load the code
-  if not window.location.hash  then window.location.hash = "optics"
+  if not window.location.search then window.location.search = "?optics/"
+  programId = window.location.search.slice(1)
 
   # Offer to load code from the cache if we can
   loadedCode = false
-  cachedCodeJson = loadCodeFromCache()
+  cachedCodeJson = loadCodeFromCache(programId)
   if cachedCodeJson
     if window.confirm("You had made changes to this code. Should we load your last version?")
       setCodeFromCache(cachedCodeJson)
       loadedCode = true
     else 
-      clearCodeInCache()
-
-  # Otherwise just load from the default "optics" directory
+      clearCodeInCache(programId)
+
+  # Otherwise just load from a local directory
   if not loadedCode
-    for [id, url] in [["modelEditor", "optics/model.json"], 
-                      ["assetsEditor", "optics/assets.json"], 
-                      ["actionsEditor", "optics/actions.js"], 
-                      ["toolsEditor", "optics/tools.js"],
-                      ["layoutEditor", "optics/layout.json"],
-                      ["servicesEditor", "optics/services.json"]]
-      loadIntoEditor(editors[id], url)
-
-  for id in ["modelEditor", "assetsEditor", "actionsEditor", "toolsEditor", "layoutEditor", "servicesEditor"]
-    editors[id].getSession().on "change", -> notifyCodeChange()
-
-  # TODO: find another way to include global data
-  globals.editors = editors
-
-  # Setup event handlers
-  $(window).on "onresize", handleResize
-
-  # Load common assets
-  GE.loadAssets EVAL_ASSETS, (err, loadedAssets) ->
-    if err then return showMessage(MessageType.Error, "Cannot load common assets")
-
-    # Make this globally available
-    evalLoadedAssets = (script for name, script of loadedAssets)
-    # Load code
-    notifyCodeChange()
-
+    ajaxRequests = (loadIntoEditor(editors[id], "#{programId}#{url}") for [id, url] in EDITOR_URL_PAIRS)
+    $.when(ajaxRequests...).fail(-> showMessage(MessageType.Error, "Cannot load game files")).then ->
+      # Load common assets
+      GE.loadAssets EVAL_ASSETS, (err, loadedAssets) ->
+        if err then return showMessage(MessageType.Error, "Cannot load common assets")
+
+        # Make this globally available
+        evalLoadedAssets = (script for name, script of loadedAssets)
+
+        # Setup event handlers on code change
+        for id in ["modelEditor", "assetsEditor", "actionsEditor", "toolsEditor", "layoutEditor", "servicesEditor"]
+          editors[id].getSession().on "change", -> notifyCodeChange()
+
+        # Load code
+        notifyCodeChange()
+

    editor.selection.clearSelection() 

# Remove code in LocalStorage
clearCodeInCache = -> 
  programId = window.location.hash.slice(1)
  if !programId then throw new Error("No program ID to remove")

  localStorage.removeItem(programId)

# Add the factory so it can be used later
registerService = (name, factory) -> services[name] = factory

  requestAnimationFrame(handleAnimation)

# Saves all editor contents to LocalStorage
saveCodeToCache = (programId) ->
  # TODO: should check that currentFrame == 0 before saving model
  codeToCache = {}
  for id, editor of editors
