diff --git a/src/main.cc b/src/main.cc
index 3f218df..7e4ab90 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -83,6 +83,9 @@ int main() {
     return EXIT_CODE_GLAD_LOADER_FAILED;
   }
 
+  SDL_SetRelativeMouseMode(SDL_TRUE);
+  SDL_WarpMouseInWindow(window, WINDOW_HALF_WIDTH, WINDOW_HALF_HEIGHT);
+
   // Set texture options
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
@@ -205,21 +208,33 @@ int main() {
       glm::vec3(-3.3f,  2.0f, -1.5f)  
   };
 
-  glm::vec3 camera_target    = glm::vec3(0.0f, 0.0f, 0.0f);
+  const float camera_speed   = 25.0f;
+  glm::vec3 camera_position  = glm::vec3(0.0f, 0.0f, 4.0f);
+  glm::vec3 camera_forward   = glm::vec3(0.0f);
   glm::vec3 world_up         = glm::vec3(0.0f, 1.0f, 0.0f);
 
+  float yaw   = -90.0f;
+  float pitch = 0.0f;
+
   glm::mat4 model      = glm::mat4(1.0f);
   glm::mat4 view       = glm::mat4(1.0f);
   glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 0.1f, 100.0f);
 
   glUniformMatrix4fv(glGetUniformLocation(main_shader.program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
 
-  float ticks        = 0;
-  float radius       = 10.0f;
-  bool running       = true;
-  SDL_Event event    = {};
+  const float sensitivity = 0.1f;
+  int last_mouse_x        = WINDOW_HALF_WIDTH;
+  int last_mouse_y        = WINDOW_HALF_HEIGHT;
+  uint32_t last_frame     = SDL_GetTicks();
+  float delta             = 0.0f;
+  bool running            = true;
+  SDL_Event event         = {};
 
   while (running) {
+    uint32_t ticks = SDL_GetTicks();
+    delta          = (ticks - last_frame) * 0.001f;
+    last_frame     = ticks;
+
     while (SDL_PollEvent(&event)) {
       switch (event.type) {
         case SDL_QUIT:
@@ -232,6 +247,36 @@ int main() {
             texture_mix_factor += 0.1f;
           } else if (event.key.keysym.sym == SDLK_DOWN) {
             texture_mix_factor -= 0.1f;
+          } else if (event.key.keysym.sym == SDLK_w) {
+            camera_position += camera_speed * delta * camera_forward;
+          } else if (event.key.keysym.sym == SDLK_s) {
+            camera_position -= camera_speed * delta * camera_forward;
+          } else if (event.key.keysym.sym == SDLK_d) {
+            camera_position += camera_speed * delta * glm::normalize(glm::cross(camera_forward, world_up));
+          } else if (event.key.keysym.sym == SDLK_a) {
+            camera_position -= camera_speed * delta * glm::normalize(glm::cross(camera_forward, world_up));
+          }
+          break;
+        case SDL_MOUSEMOTION: {
+            float x_offset = event.motion.xrel;
+            float y_offset = -event.motion.yrel;
+
+            last_mouse_x = last_mouse_x + event.motion.xrel;
+            last_mouse_y = last_mouse_y + event.motion.yrel;
+
+            x_offset *= sensitivity;
+            y_offset *= sensitivity;
+
+            yaw   += x_offset;
+            pitch += y_offset;
+
+            if(pitch > 89.0f) {
+              pitch =  89.0f;
+            }
+
+            if(pitch < -89.0f) {
+              pitch = -89.0f;
+            }
           }
           break;
         case SDL_WINDOWEVENT:
@@ -243,21 +288,22 @@ int main() {
             int w, h;
             SDL_GL_GetDrawableSize(wnd, &w, &h);
             glViewport(0, 0, w, h);
+            SDL_WarpMouseInWindow(wnd, (int)(w * 0.5f), (int)(h * 0.5f));
           }
           break;
       }
     }
 
-
     texture_mix_factor = texture_mix_factor < 0.0f ?
                          0.0f : texture_mix_factor > 1.0f ?
                          1.0f : texture_mix_factor;
 
-    ticks                     = (float)SDL_GetTicks() / 1000.0f;
-    float camera_x            = sin(ticks) * radius;
-    float camera_z            = cos(ticks) * radius;
-    glm::vec3 camera_position = glm::vec3(camera_x, 0.0f, camera_z);
-    view                      = glm::lookAt(camera_position, camera_target, world_up);
+    camera_forward.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
+    camera_forward.y = sin(glm::radians(pitch));
+    camera_forward.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
+    camera_forward   = glm::normalize(camera_forward);
+
+    view = glm::lookAt(camera_position, camera_position + camera_forward, world_up);
     glUniformMatrix4fv(glGetUniformLocation(main_shader.program, "view"), 1, GL_FALSE, glm::value_ptr(view));
 
     glClearColor(0.2f, 0.3f, 0.3f, 1.0f);