diff --git a/src/raytracer/main.c b/src/raytracer/main.c
index 6cb6a77..0c4847b 100644
--- a/src/raytracer/main.c
+++ b/src/raytracer/main.c
@@ -15,9 +15,28 @@ typedef struct {
   colour_t colour;
 } sphere_t;
 
+typedef enum {
+  LIGHT_TYPE_POINT,
+  LIGHT_TYPE_DIRECTIONAL,
+  LIGHT_TYPE_AMBIENT,
+
+  COUNT_LIGHT_TYPE,
+} light_type_t;
+
+typedef struct {
+  light_type_t type;
+  f32 intensity;
+  union {
+    vec3f_t position;
+    vec3f_t direction;
+  };
+} light_t;
+
 typedef struct {
   sphere_t *spheres;
-  u32 count;
+  light_t *lights;
+  u32 spheres_count;
+  u32 lights_count;
 } scene_t;
 
 typedef struct {
@@ -29,6 +48,9 @@ solutions_t ray_intersects_sphere(vec3f_t origin, vec3f_t direction,
                                   sphere_t sphere);
 colour_t trace_ray(vec3f_t origin, vec3f_t direction, f32 t_min, f32 t_max,
                    const scene_t *scene, colour_t default_colour);
+f32 compute_lighting(vec3f_t P, vec3f_t N, const scene_t *scene);
+f32 light_diffuse(f32 light_intensity, vec3f_t light_direction,
+                  vec3f_t surface_normal);
 
 i32 main(i32 argc, char *argv[]) {
   colour_t bg =
@@ -67,11 +89,37 @@ i32 main(i32 argc, char *argv[]) {
               (colour_t){
                   .rgba.r = 171, .rgba.g = 52, .rgba.b = 40, .rgba.a = 255},
       },
+      (sphere_t){
+          .radius = 5000.0f,
+          .centre = (vec3f_t){.x = 0.0f, .y = -5001.0f, .z = 0.0f},
+          .colour =
+              (colour_t){
+                  .rgba.r = 255, .rgba.g = 255, .rgba.b = 0, .rgba.a = 255},
+      },
+  };
+
+  light_t lights[] = {
+      (light_t){
+          .type = LIGHT_TYPE_AMBIENT,
+          .intensity = 0.2f,
+      },
+      (light_t){
+          .type = LIGHT_TYPE_POINT,
+          .intensity = 0.6f,
+          .position = (vec3f_t){.x = 2.0f, .y = 1.0f, .z = 0.0f},
+      },
+      (light_t){
+          .type = LIGHT_TYPE_DIRECTIONAL,
+          .intensity = 0.2f,
+          .direction = (vec3f_t){.x = 1.0f, .y = 4.0f, .z = 4.0f},
+      },
   };
 
   scene_t scene = {
       .spheres = spheres,
-      .count = ARR_LEN(spheres),
+      .lights = lights,
+      .spheres_count = ARR_LEN(spheres),
+      .lights_count = ARR_LEN(lights),
   };
 
   i32 w_min = ((i32)window.half_width) * -1;
@@ -131,7 +179,7 @@ colour_t trace_ray(vec3f_t origin, vec3f_t direction, f32 t_min, f32 t_max,
   f32 closest_t = INFINITY;
   sphere_t *closest_sphere = NULL;
 
-  for (u32 i = 0; i < scene->count; ++i) {
+  for (u32 i = 0; i < scene->spheres_count; ++i) {
     solutions_t solutions =
         ray_intersects_sphere(origin, direction, scene->spheres[i]);
 
@@ -152,5 +200,63 @@ colour_t trace_ray(vec3f_t origin, vec3f_t direction, f32 t_min, f32 t_max,
     return default_colour;
   }
 
-  return closest_sphere->colour;
+  vec3f_t P =
+      vec_add(vec3f_t, origin, vec_mul_num(vec3f_t, direction, closest_t));
+  vec3f_t N = vec_sub(vec3f_t, P, closest_sphere->centre);
+  N = vec_div_num(vec3f_t, N, vec_magnitude(vec3f_t, N));
+
+  f32 light = compute_lighting(P, N, scene);
+
+  return (colour_t){.rgba.r = closest_sphere->colour.rgba.r * light,
+                    .rgba.g = closest_sphere->colour.rgba.g * light,
+                    .rgba.b = closest_sphere->colour.rgba.b * light,
+                    .rgba.a = closest_sphere->colour.rgba.a};
+}
+
+f32 compute_lighting(vec3f_t P, vec3f_t N, const scene_t *scene) {
+  f32 I = 0.0f;
+  light_t light = {0};
+
+  for (u32 i = 0; i < scene->lights_count; ++i) {
+    light = scene->lights[i];
+
+    if (light.type == LIGHT_TYPE_AMBIENT) {
+      I += light.intensity;
+    } else {
+      vec3f_t L = {0};
+
+      switch (light.type) {
+      case LIGHT_TYPE_POINT:
+        L = vec_sub(vec3f_t, light.position, P);
+        break;
+      case LIGHT_TYPE_DIRECTIONAL:
+        L = light.direction;
+        break;
+      default:
+        break;
+      }
+
+      I += light_diffuse(light.intensity, L, N);
+    }
+  }
+
+  return I;
+}
+
+f32 light_diffuse(f32 light_intensity, vec3f_t light_direction,
+                  vec3f_t surface_normal) {
+  f32 dot_product = vec_dot(vec3f_t, light_direction, surface_normal);
+
+  if (dot_product < 0.0f) {
+    return 0.0f;
+  }
+
+  f32 divisor = vec_magnitude(vec3f_t, light_direction) *
+                vec_magnitude(vec3f_t, surface_normal);
+
+  if (divisor == 0.0f) {
+    return 0.0f;
+  }
+
+  return light_intensity * dot_product / divisor;
 }