diff --git a/lab4/src/lab4.js b/lab4/src/lab4.js
index 66f21ad72da0914a535204993838a2a23397161b..ce33ced2ec639ee9cdcfd4672d26c2fbcbbc52e7 100644
--- a/lab4/src/lab4.js
+++ b/lab4/src/lab4.js
@@ -27,9 +27,9 @@ const VSHADER_SOURCE =
 'uniform mat4 u_ModelMatrix;\n' +    // Model matrix
 'uniform mat4 u_NormalMatrix;\n' +   // Transformation matrix of the normal
 'uniform bool u_Clicked;\n' + // Mouse is pressed
-// 'uniform mat4 u_MvpMatrixFromLight;\n' +
+'uniform mat4 u_MvpMatrixFromLight;\n' +
 
-// 'varying vec4 v_PositionFromLight;\n' +
+'varying vec4 v_PositionFromLight;\n' +
 'varying vec4 v_Color;\n' +
 'varying vec3 v_Normal;\n' +
 'varying vec3 v_Position;\n' +
@@ -37,7 +37,7 @@ const VSHADER_SOURCE =
   'gl_Position = u_MvpMatrix * a_Position;\n' +
   // Calculate the vertex position in the world coordinate
   'v_Position = vec3(u_ModelMatrix * a_Position);\n' +
-  // 'v_PositionFromLight = u_MvpMatrixFromLight * a_Position;\n' +
+  'v_PositionFromLight = u_MvpMatrixFromLight * a_Position;\n' +
   'v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' +
   'if (u_Clicked) {\n' + //  Draw in red if mouse is pressed
   '  v_Color = vec4(1.0, 0.0, 0.0, 1.0);\n' +
@@ -57,7 +57,7 @@ const FSHADER_SOURCE =
 'uniform vec3 u_AmbientLight;\n'+   // Ambient light color
 'uniform float u_Shininess;\n'+
 'uniform bool u_LightType;\n' +
-// 'uniform sampler2D u_ShadowMap;\n' +
+'uniform sampler2D u_ShadowMap;\n' +
 
 'varying vec4 v_PositionFromLight;\n' +
 'varying vec3 v_Normal;\n' +
@@ -80,12 +80,12 @@ const FSHADER_SOURCE =
   // Calculate the final color from diffuse reflection and ambient reflection
   'vec3 diffuse = u_LightColor * v_Color.rgb * nDotl;\n' +
   'vec3 ambient = u_AmbientLight * v_Color.rgb;\n' +
-  // 'vec3 shadowCoord = (v_PositionFromLight.xyz/v_PositionFromLight.w)/2.0 + 0.5;\n' +
-  // 'vec4 rgbaDepth = texture2D(u_ShadowMap, shadowCoord.xy);\n' +
-  // 'float depth = rgbaDepth.r;\n' + // Retrieve the z-value from R
-  // 'float visibility = (shadowCoord.z > depth + 0.005) ? 0.7 : 1.0;\n' +
-  'gl_FragColor = vec4(diffuse + ambient, v_Color.a);\n' +
-  'gl_FragColor.rgb += specular * u_LightColor;\n' +
+  'vec3 shadowCoord = (v_PositionFromLight.xyz/v_PositionFromLight.w)/2.0 + 0.5;\n' +
+  'vec4 rgbaDepth = texture2D(u_ShadowMap, shadowCoord.xy);\n' +
+  'float depth = rgbaDepth.r;\n' + // Retrieve the z-value from R
+  'float visibility = (shadowCoord.z > depth + 0.005) ? 0.7 : 1.0;\n' +
+  'gl_FragColor = vec4(diffuse * visibility + specular * u_LightColor + ambient, v_Color.a);\n' +
+  // 'gl_FragColor.rgb += specular * u_LightColor;\n' +
   // 'gl_FragColor = vec4(v_Color.rgb * visibility, v_Color.a);\n' +
 '}\n';
 
@@ -127,14 +127,14 @@ function main() {
     return;
   }
 
-  // // Initialize shaders for generating a shadow map
-  // var shadowProgram = createProgram(gl, SHADOW_VSHADER_SOURCE, SHADOW_FSHADER_SOURCE);
-  // shadowProgram.a_Position = gl.getAttribLocation(shadowProgram, 'a_Position');
-  // shadowProgram.u_MvpMatrix = gl.getUniformLocation(shadowProgram, 'u_MvpMatrix');
-  // if (shadowProgram.a_Position < 0 || !shadowProgram.u_MvpMatrix) {
-  //   console.log('Failed to get the storage location of attribute or uniform variable from shadowProgram'); 
-  //   return;
-  // }
+  // Initialize shaders for generating a shadow map
+  var shadowProgram = createProgram(gl, SHADOW_VSHADER_SOURCE, SHADOW_FSHADER_SOURCE);
+  shadowProgram.a_Position = gl.getAttribLocation(shadowProgram, 'a_Position');
+  shadowProgram.u_MvpMatrix = gl.getUniformLocation(shadowProgram, 'u_MvpMatrix');
+  if (shadowProgram.a_Position < 0 || !shadowProgram.u_MvpMatrix) {
+    console.log('Failed to get the storage location of attribute or uniform variable from shadowProgram'); 
+    return;
+  }
 
   // Initialize shaders for regular drawing
   var normalProgram = createProgram(gl, VSHADER_SOURCE, FSHADER_SOURCE);
@@ -146,23 +146,23 @@ function main() {
   normalProgram.u_AmbientLight = gl.getUniformLocation(normalProgram, 'u_AmbientLight');
   normalProgram.u_Shininess = gl.getUniformLocation(normalProgram, 'u_Shininess');
   normalProgram.u_LightType = gl.getUniformLocation(normalProgram, 'u_LightType');
-  // normalProgram.u_ShadowMap = gl.getUniformLocation(normalProgram, 'u_ShadowMap');
+  normalProgram.u_ShadowMap = gl.getUniformLocation(normalProgram, 'u_ShadowMap');
   normalProgram.u_MvpMatrix = gl.getUniformLocation(normalProgram, 'u_MvpMatrix');
   normalProgram.u_ModelMatrix = gl.getUniformLocation(normalProgram, 'u_ModelMatrix');
   normalProgram.u_NormalMatrix = gl.getUniformLocation(normalProgram, 'u_NormalMatrix');
-  // normalProgram.u_MvpMatrixFromLight = gl.getUniformLocation(normalProgram, 'u_MvpMatrixFromLight');
+  normalProgram.u_MvpMatrixFromLight = gl.getUniformLocation(normalProgram, 'u_MvpMatrixFromLight');
   normalProgram.u_Clicked = gl.getUniformLocation(normalProgram, 'u_Clicked');
   if (
     !normalProgram.u_LightPosition  ||
     !normalProgram.u_LightColor     ||
     !normalProgram.u_AmbientLight   ||
     !normalProgram.u_Shininess      ||
-    // !normalProgram.u_ShadowMap      ||
+    !normalProgram.u_ShadowMap      ||
     !normalProgram.u_MvpMatrix      ||
     !normalProgram.u_ModelMatrix    ||
     // !normalProgram.u_MvpMatrix      ||
-    !normalProgram.u_NormalMatrix
-    // !normalProgram.u_MvpMatrixFromLight
+    !normalProgram.u_NormalMatrix ||
+    !normalProgram.u_MvpMatrixFromLight
   ) { 
     console.log('Failed to get the storage locations');
     return;
@@ -242,33 +242,33 @@ function main() {
       gl.uniform1i(normalProgram.u_LightType, light_type);
       // Set the light direction (in the world coordinate)
 
-      // viewProjMatrixFromLight.setPerspective(30, canvas.width/canvas.height, 1, 100);
-      // viewProjMatrixFromLight.lookAt(x_light, y_light, z_light, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
+      viewProjMatrixFromLight.setPerspective(30, SHADOW_X_Y/SHADOW_X_Y, 1, 100);
+      viewProjMatrixFromLight.lookAt(x_light, y_light, z_light, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
       
       viewProjMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
       viewProjMatrix.lookAt(eye_x, eye_y, eye_z, 0, 0.5, 0, 0, 1, 0);
 
-      // // gl.bindFramebuffer(gl.FRAMEBUFFER, shadow_frame_buffer);               // Change the drawing destination to FBO
-      // // gl.viewport(0, 0, OFFSCREEN_HEIGHT, OFFSCREEN_HEIGHT); // Set view port for FBO
-      // gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);   // Clear FBO    
+      gl.bindFramebuffer(gl.FRAMEBUFFER, shadow_frame_buffer);               // Change the drawing destination to FBO
+      gl.viewport(0, 0, SHADOW_X_Y, SHADOW_X_Y); // Set view port for FBO
+      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);   // Clear FBO    
   
-      // gl.useProgram(shadowProgram); // Set shaders for generating a shadow map
-      // // Draw the frustrum and the plane (for generating a shadow map)
-      // drawfrustrum(gl, shadowProgram, frustrum, viewProjMatrixFromLight);
-      // mvpMatrixFromLight_t.set(g_mvpMatrix); // Used later
-      // drawPlane(gl, shadowProgram, plane, viewProjMatrixFromLight);
-      // mvpMatrixFromLight_p.set(g_mvpMatrix); // Used later
+      gl.useProgram(shadowProgram); // Set shaders for generating a shadow map
+      // Draw the frustrum and the plane (for generating a shadow map)
+      drawfrustrum(gl, shadowProgram, frustrum, viewProjMatrixFromLight);
+      mvpMatrixFromLight_t.set(g_mvpMatrix); // Used later
+      drawPlane(gl, shadowProgram, plane, viewProjMatrixFromLight);
+      mvpMatrixFromLight_p.set(g_mvpMatrix); // Used later
   
-      // gl.bindFramebuffer(gl.FRAMEBUFFER, null);               // Change the drawing destination to color buffer
-      // gl.viewport(0, 0, canvas.width, canvas.height);
+      gl.bindFramebuffer(gl.FRAMEBUFFER, null);               // Change the drawing destination to color buffer
+      gl.viewport(0, 0, canvas.width, canvas.height);
       gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);    // Clear color and depth buffer
   
       gl.useProgram(normalProgram); // Set the shader for regular drawing
-      // gl.uniform1i(normalProgram.u_ShadowMap, 0);  // Pass 0 because gl.TEXTURE0 is enabled
+      gl.uniform1i(normalProgram.u_ShadowMap, 0);  // Pass 0 because gl.TEXTURE0 is enabled
       // Draw the frustrum and plane ( for regular drawing)
-      // gl.uniformMatrix4fv(normalProgram.u_MvpMatrixFromLight, false, mvpMatrixFromLight_t.elements);
+      gl.uniformMatrix4fv(normalProgram.u_MvpMatrixFromLight, false, mvpMatrixFromLight_t.elements);
       drawfrustrum(gl, normalProgram, frustrum, viewProjMatrix);
-      // gl.uniformMatrix4fv(normalProgram.u_MvpMatrixFromLight, false, mvpMatrixFromLight_p.elements);
+      gl.uniformMatrix4fv(normalProgram.u_MvpMatrixFromLight, false, mvpMatrixFromLight_p.elements);
       drawPlane(gl, normalProgram, plane, viewProjMatrix);
     }
     window.requestAnimationFrame(tick, canvas);