diff --git a/README.md b/README.md
new file mode 100644
index 0000000..21590be
--- /dev/null
+++ b/README.md
@@ -0,0 +1,58 @@
+## Physics and Timing
+
+I want to have decoupled rendering with the ability to render partial physics simulations, so it needs to be deterministic.
+
+Based on this snippet from [this article](https://gafferongames.com/post/fix_your_timestep/)
+
+```c
+double t = 0.0;
+double dt = 0.01;
+
+double currentTime = hires_time_in_seconds();
+double accumulator = 0.0;
+
+State previous;
+State current;
+
+while ( !quit )
+{
+    double newTime = time();
+    double frameTime = newTime - currentTime;
+    if ( frameTime > 0.25 )
+        frameTime = 0.25;
+    currentTime = newTime;
+
+    accumulator += frameTime;
+
+    while ( accumulator >= dt )
+    {
+        previousState = currentState;
+        integrate( currentState, t, dt );
+        t += dt;
+        accumulator -= dt;
+    }
+
+    const double alpha = accumulator / dt;
+
+    State state = currentState * alpha + 
+        previousState * ( 1.0 - alpha );
+
+    render( state );
+}
+```
+
+or from this [medium article](https://medium.com/@tglaiel/how-to-make-your-game-run-at-60fps-24c61210fe75)
+
+```c
+while(running){
+    computeDeltaTimeSomehow();
+    accumulator += deltaTime;
+    while(accumulator >= 1.0/60.0){
+        previous_state = current_state;
+        current_state = update();
+        accumulator -= 1.0/60.0;
+    }
+    render_interpolated_somehow(previous_state, current_state, accumulator/(1.0/60.0));
+    display();
+}
+```
diff --git a/assets/fonts/DepartureMonoNerdFont-Regular.otf b/assets/fonts/DepartureMonoNerdFont-Regular.otf
deleted file mode 100644
index 4097cb3..0000000
Binary files a/assets/fonts/DepartureMonoNerdFont-Regular.otf and /dev/null differ
diff --git a/assets/fonts/DepartureMonoNerdFontMono-Regular.otf b/assets/fonts/DepartureMonoNerdFontMono-Regular.otf
deleted file mode 100644
index e950e8e..0000000
Binary files a/assets/fonts/DepartureMonoNerdFontMono-Regular.otf and /dev/null differ
diff --git a/assets/fonts/DepartureMonoNerdFontPropo-Regular.otf b/assets/fonts/DepartureMonoNerdFontPropo-Regular.otf
deleted file mode 100644
index 656a96c..0000000
Binary files a/assets/fonts/DepartureMonoNerdFontPropo-Regular.otf and /dev/null differ
diff --git a/assets/stretch.bmp b/assets/stretch.bmp
deleted file mode 100644
index 42657b6..0000000
Binary files a/assets/stretch.bmp and /dev/null differ
diff --git a/src/game_state.zig b/src/game_state.zig
index 0582f4a..6edc9ca 100644
--- a/src/game_state.zig
+++ b/src/game_state.zig
@@ -1,6 +1,9 @@
 const sdl = @import("./sdl.zig").c;
 const std = @import("std");
 const sf = @import("./slime_factory.zig");
+const atils = @import("./asset_utils.zig");
+const GameText = @import("./text.zig").GameText;
+const RGBAColor = @import("./utils/rgb_color.zig").RGBAColor;
 
 pub const GameState = struct {
     r: u8 = 0xff,
@@ -21,7 +24,66 @@ pub const GameState = struct {
         self.slime_factory.deinit();
     }
 
-    pub fn update_tick(self: *GameState) void {
+    // Physics main loop that processes variable time steps
+    pub fn update_tick(self: *GameState) !void {
+        const bg_texture = try atils.loadTexture(self.renderer, "assets/background.png");
+        defer sdl.SDL_DestroyTexture(bg_texture);
+
+        _ = sdl.SDL_SetRenderDrawColor(self.renderer, 0xff, 0xff, 0xff, 0xff);
+        _ = sdl.SDL_RenderClear(self.renderer);
+        _ = sdl.SDL_RenderCopy(self.renderer, bg_texture, null, null);
+
+        var text = try GameText.loadFromRenderedText("Press S to start / stop", RGBAColor.whiteSmoke().tosdl());
+        try text.render(
+            self,
+            .{ .x = @intCast(self.SCREEN_WIDTH - text.w), .y = 2 },
+        );
+        text = try GameText.loadFromRenderedText("Press P to pause / unpause", RGBAColor.whiteSmoke().tosdl());
+        try text.render(
+            self,
+            .{ .x = @intCast(self.SCREEN_WIDTH - text.w), .y = 24 },
+        );
+
+        // var buf: [16]u8 = undefined;
+        // const timer_ms = timer.getTicks();
+        // // const timer_base = std.math.log10(timer_ms) + 3;
+        // const time_str: [*:0]const u8 = try std.fmt.bufPrintZ(&buf, "{d}ms", .{timer_ms});
+        // var time_text = try GameText.loadFromRenderedText(time_str, RGBAColor.whiteSmoke().tosdl());
+        // try time_text.render(
+        //     &game_state,
+        //     .{ .x = @intCast(game_state.SCREEN_WIDTH - time_text.w), .y = 64 },
+        // );
+
+        // const fps_ms: f32 = @floatFromInt(fps_timer.getTicks());
+        // const avg_fps: f32 = @round((frames / fps_ms) * 100_000) / 100.0;
+        // var fps_text = try GameText.loadFromRenderedText(try std.fmt.bufPrintZ(&buf, "{d} fps", .{avg_fps}), RGBAColor.whiteSmoke().tosdl());
+        // try fps_text.render(
+        //     &game_state,
+        //     .{ .x = 5, .y = 5 },
+        // );
+
+        // Render red rect
+        // const fill_rect: sdl.struct_SDL_Rect = sdl.SDL_Rect{ .x = SCREEN_WIDTH / 4, .y = SCREEN_HEIGHT / 4, .w = SCREEN_WIDTH / 2 + img_pos.x, .h = SCREEN_HEIGHT / 2 + img_pos.y };
+        // _ = sdl.SDL_SetRenderDrawColor(self.renderer, 0xff, 0x00, 0x00, 0xff);
+        // _ = sdl.SDL_RenderFillRect(self.renderer, &fill_rect);
+        // _ = sdl.SDL_RenderSetViewport(self.renderer, &.{ .x = SCREEN_WIDTH / 2, .y = SCREEN_HEIGHT / 2, .w = SCREEN_WIDTH, .h = SCREEN_HEIGHT / 2 });
+        // _ = sdl.SDL_RenderCopy(self.renderer, texture, null, null);
+        // frames += 1;
+        // if (frames > 60) {
+        //     frames = 1;
+        //     fps_timer.reset();
+        // }
+        // var dest_rect = sdl.SDL_Rect{ .x = src_rect.x + img_pos.x, .y = src_rect.y + img_pos.y, .w = SCREEN_WIDTH, .h = SCREEN_HEIGHT };
+        // _ = sdl.SDL_BlitScaled(zig_image, &src_rect, screen_surface, &dest_rect);
+
+        // _ = sdl.SDL_UpdateWindowSurface(window);
+        // sdl.SDL_Delay(100);
+
         self.slime_factory.render(self);
     }
+
+    // Renders the current frame to screen
+    pub fn render(self: *GameState) void {
+        _ = sdl.SDL_RenderPresent(self.renderer);
+    }
 };
diff --git a/src/main.zig b/src/main.zig
index 06b7f7d..1190d79 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -1,10 +1,11 @@
 const std = @import("std");
 const sdl = @import("sdl.zig").c;
-const atils = @import("./asset_utils.zig");
 const gs = @import("./game_state.zig");
 const assert = std.debug.assert;
 const SCREEN_WIDTH = 640;
 const SCREEN_HEIGHT = 480;
+const SCREEN_FPS: f64 = 60.0;
+const SCREEN_TICKS_PER_FRAME: f64 = 1000 / SCREEN_FPS;
 const log = sdl.SDL_Log;
 const GameText = @import("./text.zig").GameText;
 const RGBAColor = @import("./utils/rgb_color.zig").RGBAColor;
@@ -27,64 +28,29 @@ pub fn main() !void {
     var gpa = std.heap.GeneralPurposeAllocator(.{}){};
     const allocator = gpa.allocator();
 
-    const bg_texture = try atils.loadTexture(renderer, "assets/background.png");
-    defer sdl.SDL_DestroyTexture(bg_texture);
-
     try GameText.initFont();
     defer GameText.deinitFont();
 
-    // var slime_factory = try sf.SlimeFactory.init(allocator, renderer);
-    // defer slime_factory.deinit();
-    // try slime_factory.add(.{});
-
     var game_state = try gs.GameState.init(allocator, renderer);
     defer game_state.deinit();
 
     var quit = false;
-    var shifted = false;
-    var start_time: u32 = 0;
-    var timer = Timer{};
     var fps_timer = Timer{};
     fps_timer.start();
-    var frames: f32 = 0;
+    var frame_time = Timer{};
+    var prev_frame_ms: f64 = 16.0;
+    var accumulator: f64 = 0.0;
+
     while (!quit) {
+        frame_time.reset();
         var event: sdl.SDL_Event = undefined;
         while (sdl.SDL_PollEvent(&event) != 0) {
             switch (event.type) {
                 sdl.SDL_QUIT => {
                     quit = true;
                 },
-                sdl.SDL_KEYUP => {
-                    switch (event.key.keysym.sym) {
-                        sdl.SDLK_LSHIFT => {
-                            shifted = false;
-                        },
-                        else => {},
-                    }
-                },
                 sdl.SDL_KEYDOWN => {
                     switch (event.key.keysym.sym) {
-                        sdl.SDLK_RETURN => {
-                            start_time = sdl.SDL_GetTicks();
-                        },
-                        sdl.SDLK_s => {
-                            if (timer.status == .started) timer.stop() else timer.start();
-                        },
-                        sdl.SDLK_p => {
-                            if (timer.status == .paused) timer.unpause() else timer.pause();
-                        },
-                        // sdl.SDLK_UP => {
-                        //     img_pos.y = img_pos.y - 5;
-                        // },
-                        // sdl.SDLK_DOWN => {
-                        //     img_pos.y = img_pos.y + 5;
-                        // },
-                        // sdl.SDLK_LEFT => {
-                        //     img_pos.x = img_pos.x - 5;
-                        // },
-                        // sdl.SDLK_RIGHT => {
-                        //     img_pos.x = img_pos.x + 5;
-                        // },
                         else => {},
                     }
                     log("Got key event: %i\n", event.key.keysym.sym);
@@ -92,52 +58,43 @@ pub fn main() !void {
                 else => {},
             }
         }
-        _ = sdl.SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0xff);
-        _ = sdl.SDL_RenderClear(renderer);
-        _ = sdl.SDL_RenderCopy(renderer, bg_texture, null, null);
-        game_state.update_tick();
-        var text = try GameText.loadFromRenderedText("Press S to start / stop", RGBAColor.whiteSmoke().tosdl());
-        try text.render(
-            &game_state,
-            .{ .x = @intCast(game_state.SCREEN_WIDTH - text.w), .y = 2 },
-        );
-        text = try GameText.loadFromRenderedText("Press P to pause / unpause", RGBAColor.whiteSmoke().tosdl());
-        try text.render(
-            &game_state,
-            .{ .x = @intCast(game_state.SCREEN_WIDTH - text.w), .y = 24 },
-        );
 
-        var buf: [16]u8 = undefined;
-        const timer_ms = timer.getTicks();
-        // const timer_base = std.math.log10(timer_ms) + 3;
-        const time_str: [*:0]const u8 = try std.fmt.bufPrintZ(&buf, "{d}ms", .{timer_ms});
-        var time_text = try GameText.loadFromRenderedText(time_str, RGBAColor.whiteSmoke().tosdl());
-        try time_text.render(
-            &game_state,
-            .{ .x = @intCast(game_state.SCREEN_WIDTH - time_text.w), .y = 64 },
-        );
+        // double newTime = time();
+        // double frameTime = newTime - currentTime;
+        // if ( frameTime > 0.25 )
+        //     frameTime = 0.25;
+        // currentTime = newTime;
 
-        const fps_time: f32 = @floatFromInt(fps_timer.getTicks() / 1000);
-        const avg_fps: f32 = @round((frames / fps_time) * 100) / 100.0;
-        var fps_text = try GameText.loadFromRenderedText(try std.fmt.bufPrintZ(&buf, "{d} fps", .{avg_fps}), RGBAColor.whiteSmoke().tosdl());
-        try fps_text.render(
-            &game_state,
-            .{ .x = 5, .y = 5 },
-        );
+        // accumulator += frameTime;
 
-        // Render red rect
-        // const fill_rect: sdl.struct_SDL_Rect = sdl.SDL_Rect{ .x = SCREEN_WIDTH / 4, .y = SCREEN_HEIGHT / 4, .w = SCREEN_WIDTH / 2 + img_pos.x, .h = SCREEN_HEIGHT / 2 + img_pos.y };
-        // _ = sdl.SDL_SetRenderDrawColor(renderer, 0xff, 0x00, 0x00, 0xff);
-        // _ = sdl.SDL_RenderFillRect(renderer, &fill_rect);
-        // _ = sdl.SDL_RenderSetViewport(renderer, &.{ .x = SCREEN_WIDTH / 2, .y = SCREEN_HEIGHT / 2, .w = SCREEN_WIDTH, .h = SCREEN_HEIGHT / 2 });
-        // _ = sdl.SDL_RenderCopy(renderer, texture, null, null);
-        _ = sdl.SDL_RenderPresent(renderer);
-        frames += 1;
-        // var dest_rect = sdl.SDL_Rect{ .x = src_rect.x + img_pos.x, .y = src_rect.y + img_pos.y, .w = SCREEN_WIDTH, .h = SCREEN_HEIGHT };
-        // _ = sdl.SDL_BlitScaled(zig_image, &src_rect, screen_surface, &dest_rect);
+        // while ( accumulator >= dt )
+        // {
+        //     previousState = currentState;
+        //     integrate( currentState, t, dt );
+        //     t += dt;
+        //     accumulator -= dt;
+        // }
 
-        // _ = sdl.SDL_UpdateWindowSurface(window);
-        // sdl.SDL_Delay(100);
+        // const double alpha = accumulator / dt;
+
+        // State state = currentState * alpha +
+        //     previousState * ( 1.0 - alpha );
+
+        // render( state );
+        accumulator = accumulator + prev_frame_ms;
+        while (accumulator >= SCREEN_TICKS_PER_FRAME) {
+            try game_state.update_tick();
+            accumulator = accumulator - SCREEN_TICKS_PER_FRAME;
+            std.debug.print("Tick update done. Acc: {d}\n", .{accumulator});
+        }
+
+        game_state.render();
+
+        prev_frame_ms = @floatFromInt(frame_time.getTicks());
+        // const frame_time_ticks = frame_time.getTicks();
+        // if (frame_time_ticks < SCREEN_TICKS_PER_FRAME) {
+        //     sdl.SDL_Delay(SCREEN_TICKS_PER_FRAME - frame_time_ticks);
+        // }
     }
     return;
 }
diff --git a/src/utils/timer.zig b/src/utils/timer.zig
index 02d43db..8abe812 100644
--- a/src/utils/timer.zig
+++ b/src/utils/timer.zig
@@ -21,6 +21,7 @@ pub const Timer = struct {
         self.pause_ticks_ms = 0;
     }
 
+    // Resets the timer to 0 and starts it
     pub fn reset(self: *Timer) void {
         self.status = .started;
         self.start_ticks_ms = sdl.SDL_GetTicks();