diff --git a/Week-2/Task-4/vanilla/index.html b/Week-2/Task-4/vanilla/index.html
index ff80367..f2183c1 100644
--- a/Week-2/Task-4/vanilla/index.html
+++ b/Week-2/Task-4/vanilla/index.html
@@ -5,13 +5,119 @@
Task4 vanilla
+
+
Task 4: Group Motion
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Simple parent group animation. Keys: 1, 2, 3.
+
diff --git a/Week-2/Task-4/vanilla/main.js b/Week-2/Task-4/vanilla/main.js
index e69de29..66e11ee 100644
--- a/Week-2/Task-4/vanilla/main.js
+++ b/Week-2/Task-4/vanilla/main.js
@@ -0,0 +1,166 @@
+import * as THREE from 'three'
+
+const scene = new THREE.Scene()
+scene.background = new THREE.Color(0xedf3fa)
+
+const camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 100)
+
+const renderer = new THREE.WebGLRenderer({ antialias: true })
+renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
+renderer.setSize(window.innerWidth, window.innerHeight)
+document.body.appendChild(renderer.domElement)
+
+scene.add(new THREE.AmbientLight(0xffffff, 0.8))
+
+const keyLight = new THREE.DirectionalLight(0xffffff, 1)
+keyLight.position.set(4, 5, 4)
+scene.add(keyLight)
+
+const floor = new THREE.Mesh(
+ new THREE.PlaneGeometry(12, 12),
+ new THREE.MeshStandardMaterial({ color: 0xdde7f3, roughness: 0.95 })
+)
+floor.rotation.x = -Math.PI / 2
+floor.position.y = -1.2
+scene.add(floor)
+
+const parentGroup = new THREE.Group()
+
+const leftBox = new THREE.Mesh(
+ new THREE.BoxGeometry(0.9, 0.9, 0.9),
+ new THREE.MeshStandardMaterial({ color: 0x2a9d8f })
+)
+leftBox.position.x = -1.1
+
+const rightSphere = new THREE.Mesh(
+ new THREE.SphereGeometry(0.55, 24, 24),
+ new THREE.MeshStandardMaterial({ color: 0xe76f51 })
+)
+rightSphere.position.x = 1.1
+
+const topCone = new THREE.Mesh(
+ new THREE.ConeGeometry(0.42, 0.85, 24),
+ new THREE.MeshStandardMaterial({ color: 0x457b9d })
+)
+topCone.position.set(0, 0.95, 0)
+
+parentGroup.add(leftBox, rightSphere, topCone)
+scene.add(parentGroup)
+
+const STEPS = [
+ {
+ label: 'Step 1: Group at rest',
+ detail: 'Parent group is centered and children keep their offsets.',
+ cameraPosition: [0, 1.7, 6],
+ lookAt: [0, 0, 0],
+ groupPosition: [0, 0, 0],
+ groupRotation: [0, 0, 0],
+ spinSpeed: 0,
+ },
+ {
+ label: 'Step 2: Parent rotation',
+ detail: 'Only parent rotates; children move together without changing local layout.',
+ cameraPosition: [1.7, 1.8, 5.6],
+ lookAt: [0, 0, 0],
+ groupPosition: [0, 0, 0],
+ groupRotation: [0.15, 0.55, 0],
+ spinSpeed: 0.015,
+ },
+ {
+ label: 'Step 3: Parent move + rotate',
+ detail: 'Parent shifts and rotates as one unit while child spacing stays intact.',
+ cameraPosition: [0.6, 2.4, 7],
+ lookAt: [0.4, 0.35, 0],
+ groupPosition: [0.7, 0.4, -0.4],
+ groupRotation: [0.35, 1.2, 0.2],
+ spinSpeed: 0.03,
+ },
+]
+
+const targets = {
+ cameraPosition: new THREE.Vector3(),
+ cameraLookAt: new THREE.Vector3(),
+ lookCurrent: new THREE.Vector3(),
+ groupPosition: new THREE.Vector3(),
+ groupRotation: new THREE.Euler(),
+}
+
+let currentStepIndex = 0
+let spinSpeed = 0
+let initialized = false
+
+const stepLabel = document.getElementById('step-label')
+const stepDetail = document.getElementById('step-detail')
+const stepButtons = Array.from(document.querySelectorAll('.step-btn'))
+const prevButton = document.getElementById('prev-btn')
+const nextButton = document.getElementById('next-btn')
+
+function applyStep(stepIndex) {
+ currentStepIndex = THREE.MathUtils.clamp(stepIndex, 0, STEPS.length - 1)
+ const step = STEPS[currentStepIndex]
+
+ targets.cameraPosition.set(...step.cameraPosition)
+ targets.cameraLookAt.set(...step.lookAt)
+ targets.groupPosition.set(...step.groupPosition)
+ targets.groupRotation.set(...step.groupRotation)
+ spinSpeed = step.spinSpeed
+
+ if (!initialized) {
+ camera.position.copy(targets.cameraPosition)
+ targets.lookCurrent.copy(targets.cameraLookAt)
+ parentGroup.position.copy(targets.groupPosition)
+ parentGroup.rotation.copy(targets.groupRotation)
+ initialized = true
+ }
+
+ stepLabel.textContent = step.label
+ stepDetail.textContent = step.detail
+
+ stepButtons.forEach((button, index) => {
+ button.classList.toggle('active', index === currentStepIndex)
+ })
+
+ prevButton.disabled = currentStepIndex === 0
+ nextButton.disabled = currentStepIndex === STEPS.length - 1
+}
+
+stepButtons.forEach((button) => {
+ button.addEventListener('click', () => {
+ applyStep(Number(button.dataset.step))
+ })
+})
+
+prevButton.addEventListener('click', () => applyStep(currentStepIndex - 1))
+nextButton.addEventListener('click', () => applyStep(currentStepIndex + 1))
+
+window.addEventListener('keydown', (event) => {
+ if (event.key === '1') applyStep(0)
+ if (event.key === '2') applyStep(1)
+ if (event.key === '3') applyStep(2)
+})
+
+applyStep(0)
+
+function animate() {
+ parentGroup.position.lerp(targets.groupPosition, 0.1)
+
+ parentGroup.rotation.x += (targets.groupRotation.x - parentGroup.rotation.x) * 0.1
+ parentGroup.rotation.y += (targets.groupRotation.y - parentGroup.rotation.y) * 0.1
+ parentGroup.rotation.z += (targets.groupRotation.z - parentGroup.rotation.z) * 0.1
+
+ parentGroup.rotation.y += spinSpeed * 0.2
+
+ camera.position.lerp(targets.cameraPosition, 0.08)
+ targets.lookCurrent.lerp(targets.cameraLookAt, 0.08)
+ camera.lookAt(targets.lookCurrent)
+
+ renderer.render(scene, camera)
+}
+
+renderer.setAnimationLoop(animate)
+
+window.addEventListener('resize', () => {
+ camera.aspect = window.innerWidth / window.innerHeight
+ camera.updateProjectionMatrix()
+ renderer.setSize(window.innerWidth, window.innerHeight)
+})