diff --git a/HTMLshell.html b/HTMLshell.html
new file mode 100644
index 0000000..3af5da2
--- /dev/null
+++ b/HTMLshell.html
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+  
+  game
+
+  
+
+
+
+  
+  
+  
+
+  {{{ SCRIPT }}}
+
+  
+
+
+
diff --git a/TODO.txt b/TODO.txt
index b0bff28..ea6ee6f 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -2,6 +2,7 @@ fuck issue trackers :D
 
 =========== GENERAL ==============
 
+- 640x480 with resolution subdiv has bad sized menu item highlight
 - LOD blocks in lower res look too small
 - hitting ramps at higher speed still often bugs, try to fiddle with physics
   again (reshape iterations, tension, ...)
diff --git a/frontend_sdl.c b/frontend_sdl.c
index beaf2ce..aee6773 100644
--- a/frontend_sdl.c
+++ b/frontend_sdl.c
@@ -11,6 +11,15 @@
 
 #define LCR_FPS_GET_MS SDL_GetTicks() // uncomment for FPS measuring
 
+#ifdef __EMSCRIPTEN__
+  #define LCR_SETTING_RESOLUTION_X 640
+  #define LCR_SETTING_RESOLUTION_Y 480
+  #define LCR_SETTING_MUSIC 0
+  #define LCR_SETTING_CAR_SHADOW 0
+  #define LCR_SETTING_TEXTURE_SUBSAMPLE 4
+  #define LCR_SETTING_RESOLUTION_SUBDIVIDE 2
+#endif
+
 #include "game.h"
 
 SDL_Window *window;
@@ -18,6 +27,8 @@ SDL_Renderer *renderer;
 SDL_Texture *texture;
 SDL_Surface *screenSurface;
 
+uint8_t running = 1, fullscreen = 1;
+
 #if LCR_SETTING_332_COLOR
 uint8_t
 #else
@@ -30,6 +41,9 @@ FILE *dataFile = 0;
 
 char LCR_getNextDataFileChar(void)
 {
+#ifdef __EMSCRIPTEN__
+  return 0;
+#else
   if (!dataFile)
     return 0;
 
@@ -42,10 +56,12 @@ char LCR_getNextDataFileChar(void)
   }
 
   return c;
+#endif
 }
 
 void LCR_appendDataStr(const char *str)
 {
+#ifndef __EMSCRIPTEN__
   if (!dataFile)
     return;
 
@@ -68,6 +84,9 @@ void LCR_appendDataStr(const char *str)
       dataFile = fopen(DATA_FILE_NAME,"r");
     }
   }
+#else
+  printf("%s",str);
+#endif
 }
 
 void audioFillCallback(void *userdata, uint8_t *s, int l)
@@ -116,11 +135,26 @@ uint8_t LCR_keyPressed(uint8_t key)
   return 0;
 }
 
+#ifdef __EMSCRIPTEN__
+typedef void (*em_callback_func)(void);
+void emscripten_set_main_loop(
+  em_callback_func func, int fps, int simulate_infinite_loop);
+#endif
+
 void LCR_sleep(uint16_t timeMs)
 {
+#ifndef __EMSCRIPTEN__
   SDL_Delay(timeMs);
+#endif
 }
 
+#ifdef __EMSCRIPTEN__
+void webButton(uint8_t key, uint8_t down) // HTML button pressed
+{
+  // TODO
+}
+#endif
+
 void LCR_drawPixel(unsigned long index, uint16_t color)
 {
   screen[index] = color;
@@ -140,10 +174,36 @@ void printHelp(void)
     LCR_ARG_HELP_STR);
 }
 
+void mainLoopIteration(void)
+{
+  SDL_Event event;
+
+  running = 1;
+
+  while (SDL_PollEvent(&event))
+    if (event.type == SDL_QUIT)
+      running = 0;
+
+  keyboardState = SDL_GetKeyboardState(NULL);
+
+  running &= LCR_gameStep(SDL_GetTicks());
+
+  SDL_UpdateTexture(texture,NULL,screen,
+    LCR_SETTING_RESOLUTION_X * sizeof(
+#if LCR_SETTING_332_COLOR
+    uint8_t
+#else
+    uint16_t
+#endif
+    ));
+
+  SDL_RenderClear(renderer);
+  SDL_RenderCopy(renderer,texture,NULL,NULL);
+  SDL_RenderPresent(renderer);
+}
+
 int main(int argc, char *argv[])
 {
-  uint8_t running = 1, fullscreen = 1;
-
   for (int i = 0; i < argc; ++i)
     if (argv[i][0] == '-')
       switch (argv[i][1])
@@ -160,11 +220,20 @@ int main(int argc, char *argv[])
         default: break;
       }
 
+#ifdef __EMSCRIPTEN__
+  fullscreen = 0;
+#else
   dataFile = fopen(DATA_FILE_NAME,"r");
 
   if (!dataFile)
     LCR_log("couldn't open data file");
 
+  musicFile = fopen("assets/music","rb");
+
+  if (!musicFile)
+    fputs("could not open music file",stderr);
+#endif
+
   LCR_log("initializing game");
   LCR_gameInit(argc,(const char **) argv);
 
@@ -173,23 +242,23 @@ int main(int argc, char *argv[])
 
   LCR_log("initializing audio");
   SDL_AudioSpec audioSpec;
-  SDL_memset(&audioSpec, 0, sizeof(audioSpec));
+  SDL_memset(&audioSpec,0,sizeof(audioSpec));
   audioSpec.callback = audioFillCallback;
+  audioSpec.channels = 1;
   audioSpec.freq = 8000;
   audioSpec.format = AUDIO_U8;
-  audioSpec.channels = 1;
+
+#ifdef __EMSCRIPTEN__
+  audioSpec.samples = 1024;
+#else
   audioSpec.samples = 64;
+#endif
 
   if (SDL_OpenAudio(&audioSpec,NULL) < 0)
     fputs("could not initialize audio",stderr);
-    
+
   SDL_PauseAudio(0);
 
-  musicFile = fopen("assets/music","rb");
-
-  if (!musicFile)
-    fputs("could not open music file",stderr);
-
   window =
     SDL_CreateWindow("Licar", SDL_WINDOWPOS_UNDEFINED,
     SDL_WINDOWPOS_UNDEFINED, LCR_SETTING_RESOLUTION_X, LCR_SETTING_RESOLUTION_Y,
@@ -237,41 +306,22 @@ int main(int argc, char *argv[])
 
   LCR_log("starting game loop");
 
-  while (running)
-  {
-    SDL_Event event;
-
-    running = 1;
-
-    while (SDL_PollEvent(&event))
-      if (event.type == SDL_QUIT)
-        running = 0;
-
-    keyboardState = SDL_GetKeyboardState(NULL);
-
-    running &= LCR_gameStep(SDL_GetTicks());
-
-    SDL_UpdateTexture(texture,NULL,screen,
-      LCR_SETTING_RESOLUTION_X * sizeof(
-#if LCR_SETTING_332_COLOR
-      uint8_t
+#ifdef __EMSCRIPTEN__
+  emscripten_set_main_loop(mainLoopIteration,0,1);
 #else
-      uint16_t
+  while (running)
+    mainLoopIteration();
 #endif
-      ));
-
-    SDL_RenderClear(renderer);
-    SDL_RenderCopy(renderer,texture,NULL,NULL);
-    SDL_RenderPresent(renderer);
-  }
 
   LCR_log("ending");
 
   if (musicFile)
     fclose(musicFile);
 
+#ifndef __EMSCRIPTEN__
   if (dataFile)
     fclose(dataFile);
+#endif
 
   SDL_PauseAudio(1);
   SDL_CloseAudio();
diff --git a/make.sh b/make.sh
index ff48594..be35b8a 100755
--- a/make.sh
+++ b/make.sh
@@ -35,10 +35,14 @@ if [ $PLATFORM = "sdl" ]; then
   # - SDL2 (dev) package
 
   SDL_FLAGS=`sdl2-config --cflags --libs`
-  COMMAND="${COMPILER} ${C_FLAGS} frontend_sdl.c -I/usr/local/include ${SDL_FLAGS}"
+  ${COMPILER} ${C_FLAGS} frontend_sdl.c -I/usr/local/include ${SDL_FLAGS}
+elif [ $PLATFORM = "emscripten" ]; then
+  # emscripten (browser Javascript) build, requires:
+  # - emscripten
+#  COMMAND="../emsdk/upstream/emscripten/emcc ./frontend_sdl.c -s USE_SDL=2 -O3 -lopenal --shell-file HTMLshell.html -o licar.html -s EXPORTED_FUNCTIONS=\'[\"_main\",\"_webButton\"]\' -s EXPORTED_RUNTIME_METHODS=\'[\"ccall\",\"cwrap\"]\'"
+  ../emsdk/upstream/emscripten/emcc ./frontend_sdl.c -s USE_SDL=2 -O3 -lopenal --shell-file HTMLshell.html -o licar.html -s EXPORTED_FUNCTIONS='["_main","_webButton"]' -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]'
+else
+  echo "unknown frontend"
 fi
 
-echo ${COMMAND}
-${COMMAND}
-
 echo "done"