<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Dipankar Paul's blog]]></title><description><![CDATA[Dipankar Paul's blog]]></description><link>https://blog.iamdipankarpaul.com</link><generator>RSS for Node</generator><lastBuildDate>Wed, 08 Apr 2026 12:54:22 GMT</lastBuildDate><atom:link href="https://blog.iamdipankarpaul.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Mastering Camera Movement in Three.js]]></title><description><![CDATA[Hey there! 👋 Welcome back to my Three.js learning journey. So far, I've covered the basics, geometries, materials, lighting, OrbitControls, and transformations. Today, I'm diving into something that completely changed how I think about 3D scenes: ca...]]></description><link>https://blog.iamdipankarpaul.com/mastering-camera-movement-in-threejs</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/mastering-camera-movement-in-threejs</guid><category><![CDATA[ThreeJS]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[React]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Mon, 12 Jan 2026 13:30:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768224591154/4a1946a3-7057-4d91-9620-f0dc92e65c33.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey there! 👋 Welcome back to my Three.js learning journey. So far, I've covered the basics, geometries, materials, lighting, OrbitControls, and transformations. Today, I'm diving into something that completely changed how I think about 3D scenes: <strong>camera movement</strong>.</p>
<h2 id="heading-what-a-camera-really-is">What a Camera Really Is</h2>
<p>Before we get into movement, let's establish something important: <strong>camera isn't just a passive observer, it’s just another object in your 3D scene</strong>.</p>
<p>It has position, rotation, and direction - just like any mesh. The camera simply defines:</p>
<ul>
<li><p><strong>Where the viewer is</strong> (position)</p>
</li>
<li><p><strong>What the viewer looks at</strong> (rotation/direction)</p>
</li>
<li><p><strong>How motion is perceived</strong> (field of view, near/far planes)</p>
</li>
</ul>
<h3 id="heading-the-movie-analogy">The Movie Analogy</h3>
<p>Think of your Three.js scene like a movie set. The camera is literally the camera - it can be:</p>
<ul>
<li><p><strong>Static</strong> (fixed on a tripod, filming one angle)</p>
</li>
<li><p><strong>Orbiting</strong> (on a circular track around the actors)</p>
</li>
<li><p><strong>Following</strong> (mounted on a dolly, tracking a character)</p>
</li>
<li><p><strong>First-person</strong> (handheld, the viewer IS the character)</p>
</li>
</ul>
<p>Good camera movement makes even simple scenes feel alive and cinematic. Bad camera movement? It breaks the immersion instantly.</p>
<h2 id="heading-static-camera-the-foundation">Static Camera: The Foundation</h2>
<p>Let's start with the simplest approach - a camera that doesn't move at all.</p>
<h3 id="heading-when-to-use-it">When to Use It</h3>
<ul>
<li><p>Demos and tutorials</p>
</li>
<li><p>Debugging your scene</p>
</li>
<li><p>UI-like interfaces</p>
</li>
<li><p>Scenes where the objects move, not the viewer</p>
</li>
</ul>
<h3 id="heading-setting-up-a-static-camera">Setting Up a Static Camera</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#canvas"</span>);

<span class="hljs-comment">// Scene and renderer</span>
<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ canvas });
renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);

<span class="hljs-comment">// Create camera</span>
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(
    <span class="hljs-number">75</span>,                                  <span class="hljs-comment">// Field of view (degrees)</span>
    <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight,  <span class="hljs-comment">// Aspect ratio</span>
    <span class="hljs-number">0.1</span>,                                 <span class="hljs-comment">// Near clipping plane</span>
    <span class="hljs-number">1000</span>                                  <span class="hljs-comment">// Far clipping plane</span>
);

<span class="hljs-comment">// Position the camera</span>
camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">2</span>, <span class="hljs-number">6</span>);  <span class="hljs-comment">// x=0, y=2 (slightly up), z=6 (towards you)</span>

<span class="hljs-comment">// Make camera look at a point</span>
camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);  <span class="hljs-comment">// Look at world origin</span>

<span class="hljs-comment">// Create a simple object to view</span>
<span class="hljs-keyword">const</span> geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
<span class="hljs-keyword">const</span> material = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0x44aa88</span> });
<span class="hljs-keyword">const</span> cube = <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
scene.add(cube);

<span class="hljs-comment">// Add light</span>
<span class="hljs-keyword">const</span> light = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">1</span>);
light.position.set(<span class="hljs-number">5</span>, <span class="hljs-number">5</span>, <span class="hljs-number">5</span>);
scene.add(light);

<span class="hljs-comment">// Render</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
    requestAnimationFrame(animate);

    <span class="hljs-comment">// Rotate cube for visual interest</span>
    cube.rotation.y += <span class="hljs-number">0.01</span>;

    renderer.render(scene, camera);
}

animate();
</code></pre>
<h3 id="heading-the-magic-of-lookat">The Magic of lookAt()</h3>
<p>The <code>lookAt()</code> method is powerful - it automatically rotates the camera so it points at a specific position. Without it, the camera might be facing the wrong direction even if positioned correctly.</p>
<pre><code class="lang-javascript">camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);  <span class="hljs-comment">// Look at origin</span>
camera.lookAt(cube.position);  <span class="hljs-comment">// Look at the cube</span>
camera.lookAt(<span class="hljs-keyword">new</span> THREE.Vector3(<span class="hljs-number">5</span>, <span class="hljs-number">2</span>, <span class="hljs-number">-3</span>));  <span class="hljs-comment">// Look at specific point</span>
</code></pre>
<h2 id="heading-orbitcontrols-user-controlled-camera">OrbitControls: User-Controlled Camera</h2>
<p>OrbitControls is probably the most common camera system you'll use. It lets users rotate around a target point, zoom in/out, and pan - just like in 3D modeling software.</p>
<h3 id="heading-when-to-use-it-1">When to Use It</h3>
<ul>
<li><p>Product viewers (360° product inspection)</p>
</li>
<li><p>3D editors and tools</p>
</li>
<li><p>Architectural visualizations</p>
</li>
<li><p>Any scene where users need to explore</p>
</li>
</ul>
<h3 id="heading-setting-up-orbitcontrols">Setting Up OrbitControls</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;
<span class="hljs-keyword">import</span> { OrbitControls } <span class="hljs-keyword">from</span> <span class="hljs-string">"three/examples/jsm/controls/OrbitControls.js"</span>;

<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#canvas"</span>);
<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ canvas });
renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);

<span class="hljs-comment">// Camera</span>
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(
    <span class="hljs-number">75</span>,
    <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight,
    <span class="hljs-number">0.1</span>,
    <span class="hljs-number">1000</span>
);
camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">2</span>, <span class="hljs-number">6</span>);

<span class="hljs-comment">// Create OrbitControls</span>
<span class="hljs-keyword">const</span> controls = <span class="hljs-keyword">new</span> OrbitControls(camera, renderer.domElement);

<span class="hljs-comment">// Set what point to orbit around</span>
controls.target.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

<span class="hljs-comment">// Enable smooth damping (inertia)</span>
controls.enableDamping = <span class="hljs-literal">true</span>;
controls.dampingFactor = <span class="hljs-number">0.05</span>;  <span class="hljs-comment">// Lower = more inertia</span>

<span class="hljs-comment">// Create scene objects</span>
<span class="hljs-keyword">const</span> geometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
<span class="hljs-keyword">const</span> material = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0x44aa88</span> });
<span class="hljs-keyword">const</span> cube = <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
scene.add(cube);

<span class="hljs-keyword">const</span> light = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">1</span>);
light.position.set(<span class="hljs-number">5</span>, <span class="hljs-number">5</span>, <span class="hljs-number">5</span>);
scene.add(light);

<span class="hljs-comment">// Animation loop</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
    requestAnimationFrame(animate);

    <span class="hljs-comment">// IMPORTANT: Update controls when damping is enabled</span>
    controls.update();

    renderer.render(scene, camera);
}

animate();
</code></pre>
<h3 id="heading-key-concepts">Key Concepts</h3>
<p><strong>Camera Orbits Around Target, Not Itself</strong></p>
<p>This is crucial to understand. The camera doesn't rotate around its own position - it rotates around the <code>target</code> point. Imagine the camera is on a sphere, and it moves along that sphere's surface while always looking at the center.</p>
<p><strong>Damping Simulates Inertia</strong></p>
<p>Without damping, the camera stops instantly when you release the mouse - feels robotic. With damping, it gradually slows down - feels natural and smooth. Don't forget to call <code>controls.update()</code> in your animation loop when damping is enabled!</p>
<h2 id="heading-manual-camera-orbit-taking-control">Manual Camera Orbit: Taking Control</h2>
<p>OrbitControls is great, but what if you want a camera that orbits automatically, or follows a specific path? Time to use some math!</p>
<h3 id="heading-when-to-use-it-2">When to Use It</h3>
<ul>
<li><p>Automated camera animations</p>
</li>
<li><p>Cinematic sequences</p>
</li>
<li><p>Scripted tours</p>
</li>
<li><p>Custom camera behaviors</p>
</li>
</ul>
<h3 id="heading-creating-a-circular-orbit">Creating a Circular Orbit</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> time = <span class="hljs-number">0</span>;
<span class="hljs-keyword">const</span> radius = <span class="hljs-number">6</span>;  <span class="hljs-comment">// How far from center</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
    requestAnimationFrame(animate);

    time += <span class="hljs-number">0.01</span>;  <span class="hljs-comment">// Speed of rotation</span>

    <span class="hljs-comment">// Calculate position on a circle using sin/cos</span>
    camera.position.x = <span class="hljs-built_in">Math</span>.cos(time) * radius;
    camera.position.z = <span class="hljs-built_in">Math</span>.sin(time) * radius;
    camera.position.y = <span class="hljs-number">2</span>;  <span class="hljs-comment">// Height above ground</span>

    <span class="hljs-comment">// Always look at center</span>
    camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

    renderer.render(scene, camera);
}

animate();
</code></pre>
<h3 id="heading-understanding-the-math">Understanding the Math</h3>
<ul>
<li><p><code>Math.cos(time)</code> and <code>Math.sin(time)</code> create circular motion</p>
</li>
<li><p>Multiply by <code>radius</code> to control distance from center</p>
</li>
<li><p>Increment <code>time</code> to move around the circle</p>
</li>
<li><p><code>lookAt()</code> keeps the camera pointed at the center</p>
</li>
</ul>
<p>This is the foundation of all cinematic camera movements! You can modify this to create ellipses, figure-8 patterns, or complex paths.</p>
<h2 id="heading-camera-follow-system-tracking-objects">Camera Follow System: Tracking Objects</h2>
<p>Ever played a racing game where the camera follows your car? That's a follow system.</p>
<h3 id="heading-when-to-use-it-3">When to Use It</h3>
<ul>
<li><p>Games (following the player)</p>
</li>
<li><p>Simulations (tracking moving objects)</p>
</li>
<li><p>Animated scenes</p>
</li>
<li><p>Product showcases with moving elements</p>
</li>
</ul>
<h3 id="heading-basic-follow-camera">Basic Follow Camera</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Create a target object to follow</span>
<span class="hljs-keyword">const</span> targetGeometry = <span class="hljs-keyword">new</span> THREE.SphereGeometry(<span class="hljs-number">0.5</span>);
<span class="hljs-keyword">const</span> targetMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xff0000</span> });
<span class="hljs-keyword">const</span> target = <span class="hljs-keyword">new</span> THREE.Mesh(targetGeometry, targetMaterial);
scene.add(target);

<span class="hljs-comment">// Camera offset from target</span>
<span class="hljs-keyword">const</span> offset = <span class="hljs-keyword">new</span> THREE.Vector3(<span class="hljs-number">0</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>);  <span class="hljs-comment">// Behind and above</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
    requestAnimationFrame(animate);

    <span class="hljs-comment">// Move the target (simulate player movement)</span>
    target.position.x += <span class="hljs-number">0.02</span>;

    <span class="hljs-comment">// Camera follows target with offset</span>
    camera.position.copy(target.position).add(offset);

    <span class="hljs-comment">// Look at the target</span>
    camera.lookAt(target.position);

    renderer.render(scene, camera);
}

animate();
</code></pre>
<h3 id="heading-how-it-works">How It Works</h3>
<ol>
<li><p>The target object moves around the scene</p>
</li>
<li><p>Every frame, we copy the target's position</p>
</li>
<li><p>Add an offset to position the camera behind/above</p>
</li>
<li><p>Use <code>lookAt()</code> to point the camera at the target</p>
</li>
</ol>
<p>The camera is now "attached" to the target with a fixed offset.</p>
<h2 id="heading-smooth-camera-follow-adding-polish">Smooth Camera Follow: Adding Polish</h2>
<p>The basic follow camera works, but it feels robotic - the camera snaps to follow the target instantly. Let's make it smooth!</p>
<h3 id="heading-linear-interpolation-lerp">Linear Interpolation (Lerp)</h3>
<p>Lerp smoothly blends between two values. Instead of instantly moving to the target position, we move partway there each frame.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> offset = <span class="hljs-keyword">new</span> THREE.Vector3(<span class="hljs-number">0</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>);
<span class="hljs-keyword">const</span> desiredPosition = <span class="hljs-keyword">new</span> THREE.Vector3();

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
    requestAnimationFrame(animate);

    <span class="hljs-comment">// Move target</span>
    target.position.x += <span class="hljs-number">0.02</span>;

    <span class="hljs-comment">// Calculate where camera WANTS to be</span>
    desiredPosition.copy(target.position).add(offset);

    <span class="hljs-comment">// Smoothly move camera toward desired position</span>
    camera.position.lerp(desiredPosition, <span class="hljs-number">0.1</span>);

    <span class="hljs-comment">// Look at target</span>
    camera.lookAt(target.position);

    renderer.render(scene, camera);
}

animate();
</code></pre>
<h3 id="heading-understanding-lerp">Understanding Lerp</h3>
<pre><code class="lang-javascript">camera.position.lerp(desiredPosition, <span class="hljs-number">0.1</span>);
</code></pre>
<p>The second parameter (0.1) controls the blend:</p>
<ul>
<li><p><strong>0.1</strong> = Move 10% of the way each frame (slow, heavy feel)</p>
</li>
<li><p><strong>0.5</strong> = Move 50% of the way each frame (medium)</p>
</li>
<li><p><strong>0.9</strong> = Move 90% of the way each frame (fast, snappy)</p>
</li>
</ul>
<p>Lower values create a "camera on a spring" effect - it lags behind and smoothly catches up. This feels natural and cinematic!</p>
<h2 id="heading-first-person-camera-becoming-the-player">First-Person Camera: Becoming the Player</h2>
<p>In first-person games, the camera IS the player. Movement happens relative to where the camera is looking.</p>
<h3 id="heading-when-to-use-it-4">When to Use It</h3>
<ul>
<li><p>First-person games</p>
</li>
<li><p>Walkthroughs</p>
</li>
<li><p>Immersive experiences</p>
</li>
<li><p>VR applications</p>
</li>
</ul>
<h3 id="heading-basic-first-person-movement">Basic First-Person Movement</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> speed = <span class="hljs-number">0.05</span>;
<span class="hljs-keyword">const</span> keys = {};

<span class="hljs-comment">// Track key presses</span>
<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'keydown'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> keys[e.key] = <span class="hljs-literal">true</span>);
<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'keyup'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> keys[e.key] = <span class="hljs-literal">false</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
    requestAnimationFrame(animate);

    <span class="hljs-comment">// Move forward (in camera's local forward direction)</span>
    <span class="hljs-keyword">if</span> (keys[<span class="hljs-string">'w'</span>]) {
        camera.translateZ(-speed);  <span class="hljs-comment">// Negative Z is forward</span>
    }

    <span class="hljs-comment">// Move backward</span>
    <span class="hljs-keyword">if</span> (keys[<span class="hljs-string">'s'</span>]) {
        camera.translateZ(speed);
    }

    <span class="hljs-comment">// Strafe left</span>
    <span class="hljs-keyword">if</span> (keys[<span class="hljs-string">'a'</span>]) {
        camera.translateX(-speed);
    }

    <span class="hljs-comment">// Strafe right</span>
    <span class="hljs-keyword">if</span> (keys[<span class="hljs-string">'d'</span>]) {
        camera.translateX(speed);
    }

    renderer.render(scene, camera);
}

animate();
</code></pre>
<h3 id="heading-important-local-vs-world-space">Important: Local vs World Space</h3>
<p><code>translateZ()</code> and <code>translateX()</code> move in <strong>local space</strong> - relative to the camera's current rotation. This is perfect for first-person movement because:</p>
<ul>
<li><p>Forward is always "where the camera is looking"</p>
</li>
<li><p>Left/right moves perpendicular to that direction</p>
</li>
<li><p>It feels natural and intuitive</p>
</li>
</ul>
<p>If you used world space (<code>position.z += speed</code>), the camera would always move in the same global direction regardless of where it's facing - very disorienting!</p>
<h2 id="heading-common-mistakes-i-made-these">Common Mistakes (I Made These!)</h2>
<h3 id="heading-1-forgetting-lookat">1. Forgetting lookAt()</h3>
<p>When manually moving the camera, you need to call <code>lookAt()</code> to point it at something. Otherwise, it might face the wrong direction.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Wrong - camera might face the wrong way</span>
camera.position.set(<span class="hljs-number">5</span>, <span class="hljs-number">5</span>, <span class="hljs-number">5</span>);

<span class="hljs-comment">// Right - camera points at target</span>
camera.position.set(<span class="hljs-number">5</span>, <span class="hljs-number">5</span>, <span class="hljs-number">5</span>);
camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
</code></pre>
<h3 id="heading-2-mixing-controls-and-manual-movement">2. Mixing Controls and Manual Movement</h3>
<p>Don't use OrbitControls AND manually move the camera. Pick one approach! They'll fight each other.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Don't do this!</span>
<span class="hljs-keyword">const</span> controls = <span class="hljs-keyword">new</span> OrbitControls(camera, canvas);
camera.position.x += <span class="hljs-number">0.1</span>;  <span class="hljs-comment">// Conflicts with controls</span>
</code></pre>
<h3 id="heading-3-moving-camera-without-updating-target">3. Moving Camera Without Updating Target</h3>
<p>If you manually move the camera while using OrbitControls, update the target too:</p>
<pre><code class="lang-javascript">camera.position.set(<span class="hljs-number">10</span>, <span class="hljs-number">5</span>, <span class="hljs-number">10</span>);
controls.target.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
controls.update();  <span class="hljs-comment">// Important!</span>
</code></pre>
<h3 id="heading-4-large-lerp-values-causing-motion-sickness">4. Large Lerp Values Causing Motion Sickness</h3>
<p>Using lerp values above 0.3 can make the camera move too quickly and feel jittery. Keep it smooth:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Too fast - feels jarring</span>
camera.position.lerp(target, <span class="hljs-number">0.8</span>);

<span class="hljs-comment">// Better - smooth and natural</span>
camera.position.lerp(target, <span class="hljs-number">0.1</span>);
</code></pre>
<h2 id="heading-when-to-use-each-approach">When to Use Each Approach</h2>
<p><strong>Static Camera</strong></p>
<ul>
<li><p>Simple demos</p>
</li>
<li><p>Debugging</p>
</li>
<li><p>Fixed viewpoint scenes</p>
</li>
</ul>
<p><strong>OrbitControls</strong></p>
<ul>
<li><p>Product viewers</p>
</li>
<li><p>User exploration</p>
</li>
<li><p>3D editors</p>
</li>
<li><p>Most interactive scenes</p>
</li>
</ul>
<p><strong>Manual Orbit</strong></p>
<ul>
<li><p>Automated rotations</p>
</li>
<li><p>Cinematic sequences</p>
</li>
<li><p>Custom animations</p>
</li>
<li><p>Scripted camera paths</p>
</li>
</ul>
<p><strong>Follow Camera</strong></p>
<ul>
<li><p>Games</p>
</li>
<li><p>Tracking simulations</p>
</li>
<li><p>Following moving objects</p>
</li>
</ul>
<p><strong>Smooth Follow (Lerp)</strong></p>
<ul>
<li><p>Polished game cameras</p>
</li>
<li><p>Professional animations</p>
</li>
<li><p>Any time you want smooth, natural motion</p>
</li>
</ul>
<p><strong>First-Person</strong></p>
<ul>
<li><p>FPS games</p>
</li>
<li><p>Walkthroughs</p>
</li>
<li><p>Immersive experiences</p>
</li>
</ul>
<h2 id="heading-what-i-learned">What I Learned</h2>
<h3 id="heading-1-the-camera-is-just-an-object">1. The Camera Is Just an Object</h3>
<p>This was a lightbulb moment for me. The camera has position and rotation like everything else - there's nothing magical about it. Understanding this makes camera control much less intimidating.</p>
<h3 id="heading-2-movement-defines-emotion">2. Movement Defines Emotion</h3>
<p>A slow, smooth camera feels calm and professional. A fast, jerky camera feels chaotic or amateurish. The way you move the camera dramatically affects how your scene is perceived.</p>
<h3 id="heading-3-math-gives-you-control">3. Math Gives You Control</h3>
<p>OrbitControls is convenient, but using sin/cos to calculate camera positions gives you complete creative control. Every cinematic camera movement is just math!</p>
<h3 id="heading-4-lerp-is-your-friend">4. Lerp Is Your Friend</h3>
<p>Linear interpolation smooths out almost any motion. Whether following objects or moving between positions, lerp makes everything feel more polished and professional.</p>
<h3 id="heading-5-local-space-vs-world-space-matters">5. Local Space vs World Space Matters</h3>
<p>Understanding the difference between <code>translateZ()</code> (local) and <code>position.z +=</code> (world) is crucial, especially for first-person cameras.</p>
<h3 id="heading-6-dont-mix-control-methods">6. Don't Mix Control Methods</h3>
<p>Pick one camera control approach and stick with it. Mixing OrbitControls with manual movement creates conflicts and weird behavior.</p>
<h3 id="heading-7-lookat-is-essential">7. lookAt() Is Essential</h3>
<p>Whenever you manually position the camera, remember to call <code>lookAt()</code> to point it at your subject. I can't tell you how many times I forgot this and wondered why my camera was facing the wrong direction!</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Camera movement is one of those things that seems simple until you really dive into it. But once you understand that the camera is just another object, and you learn a few key techniques (static, orbit, follow, lerp), you can create any camera behavior you can imagine.</p>
<p>Start with static cameras to understand positioning and <code>lookAt()</code>. Then experiment with OrbitControls to see how interactive cameras work. Once you're comfortable, try creating a manual orbit with sin/cos. Finally, build a smooth follow camera with lerp.</p>
<p>The key is understanding that <strong>how you move the camera is just as important as what you're showing</strong>. Great camera work makes simple scenes feel cinematic!</p>
<p>Try building:</p>
<ul>
<li><p>A product viewer with OrbitControls</p>
</li>
<li><p>An automatic turntable with manual orbit</p>
</li>
<li><p>A simple game with a follow camera</p>
</li>
<li><p>A first-person walkthrough</p>
</li>
</ul>
<p>Once you master camera control, you'll see how it connects with everything else!</p>
<p>Happy filming! 🎥</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Transformations in Three.js]]></title><description><![CDATA[Transformations are the foundation of all 3D movement and animation. Whether you're building a game, creating an architectural visualization, or designing an interactive product showcase, understanding how to position, rotate, and scale objects is es...]]></description><link>https://blog.iamdipankarpaul.com/understanding-transformations-in-threejs</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/understanding-transformations-in-threejs</guid><category><![CDATA[ThreeJS]]></category><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Fri, 09 Jan 2026 02:30:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767594088493/9024d4a5-d6d9-4e70-969e-c603d01d8404.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Transformations are the foundation of all 3D movement and animation. Whether you're building a game, creating an architectural visualization, or designing an interactive product showcase, understanding how to position, rotate, and scale objects is essential.</p>
<p>In this post, we'll explore the three core transformations in Three.js and how they work together to bring your 3D scenes to life.</p>
<h2 id="heading-the-three-core-transformations">The Three Core Transformations</h2>
<p>Every object in Three.js has three fundamental properties that define its transformation:</p>
<ul>
<li><p><strong>Position</strong>: Where the object is located in 3D space</p>
</li>
<li><p><strong>Rotation</strong>: How the object is oriented or tilted</p>
</li>
<li><p><strong>Scale</strong>: How large or small the object appears</p>
</li>
</ul>
<p><strong>Think of a toy car on a table:</strong></p>
<ul>
<li><p><strong>Position</strong> is where you place it on the table (left, right, forward, back, up, down)</p>
</li>
<li><p><strong>Rotation</strong> is which direction it's facing and whether it's tilted</p>
</li>
<li><p><strong>Scale</strong> is like using a magnifying glass making it appear bigger or smaller</p>
</li>
</ul>
<h3 id="heading-transform-order">Transform Order</h3>
<p>Three.js applies these transformations in a specific order internally:</p>
<p><strong>Scale → Rotation → Position</strong></p>
<p>You cannot change this order. Understanding this sequence is important because it affects how your objects behave. For example, if you scale an object after rotating it, the rotation axis itself gets scaled, which can produce unexpected results.</p>
<h2 id="heading-position-moving-objects-in-3d-space">Position: Moving Objects in 3D Space</h2>
<p>Position determines where an object exists in 3D space using three coordinates: X, Y, and Z.</p>
<ul>
<li><p><strong>X axis</strong>: Left (negative) to Right (positive)</p>
</li>
<li><p><strong>Y axis</strong>: Down (negative) to Up (positive)</p>
</li>
<li><p><strong>Z axis</strong>: Into screen (negative) to Out toward you (positive)</p>
</li>
</ul>
<h3 id="heading-positioning-a-mesh">Positioning a Mesh</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#canvas"</span>);

<span class="hljs-comment">// Scene setup</span>
<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();

<span class="hljs-comment">// Camera</span>
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(
    <span class="hljs-number">75</span>,
    <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight,
    <span class="hljs-number">0.1</span>,
    <span class="hljs-number">1000</span>
);
camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>);

<span class="hljs-comment">// Renderer</span>
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ canvas });
renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);

<span class="hljs-comment">// Create a cube</span>
<span class="hljs-keyword">const</span> boxGeometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
<span class="hljs-keyword">const</span> material = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0x44aa88</span>,
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0x112244</span>,
});
<span class="hljs-keyword">const</span> cube = <span class="hljs-keyword">new</span> THREE.Mesh(boxGeometry, material);
scene.add(cube);

<span class="hljs-comment">// Position the cube</span>
cube.position.set(<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>);  <span class="hljs-comment">// x=2 (right), y=1 (up), z=0 (center)</span>

<span class="hljs-comment">// Or set individually</span>
<span class="hljs-comment">// cube.position.x = 2;</span>
<span class="hljs-comment">// cube.position.y = 1;</span>
<span class="hljs-comment">// cube.position.z = 0;</span>

<span class="hljs-comment">// Add lighting</span>
<span class="hljs-keyword">const</span> light = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">5</span>);
light.position.set(<span class="hljs-number">5</span>, <span class="hljs-number">5</span>, <span class="hljs-number">5</span>);
scene.add(light);

<span class="hljs-comment">// Render</span>
renderer.render(scene, camera);
</code></pre>
<p>The <code>position</code> property is a <code>Vector3</code> object with <code>x</code>, <code>y</code>, and <code>z</code> components. You can set all three at once with <code>position.set()</code> or individually.</p>
<h2 id="heading-rotation-orienting-objects">Rotation: Orienting Objects</h2>
<p>Rotation determines which direction an object faces and how it's tilted. In Three.js, rotations are measured in <strong>radians</strong>, not degrees.</p>
<p><strong>Quick conversion</strong>:</p>
<ul>
<li><p>360° = 2π radians (full circle)</p>
</li>
<li><p>180° = π radians</p>
</li>
<li><p>90° = π/2 radians</p>
</li>
</ul>
<h3 id="heading-rotating-a-mesh">Rotating a Mesh</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Rotate around each axis (in radians)</span>
cube.rotation.x = <span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>;  <span class="hljs-comment">// 45 degrees around X axis</span>
cube.rotation.y = <span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">2</span>;  <span class="hljs-comment">// 90 degrees around Y axis</span>
cube.rotation.z = <span class="hljs-number">0</span>;            <span class="hljs-comment">// No rotation around Z axis</span>

<span class="hljs-comment">// Or convert from degrees</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">degreesToRadians</span>(<span class="hljs-params">degrees</span>) </span>{
    <span class="hljs-keyword">return</span> degrees * (<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">180</span>);
}

cube.rotation.y = degreesToRadians(<span class="hljs-number">90</span>);  <span class="hljs-comment">// 90 degrees</span>
</code></pre>
<h3 id="heading-animated-rotation">Animated Rotation</h3>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
    requestAnimationFrame(animate);

    <span class="hljs-comment">// Increment rotation each frame for smooth animation</span>
    cube.rotation.x += <span class="hljs-number">0.01</span>;  <span class="hljs-comment">// Slow rotation around X</span>
    cube.rotation.y += <span class="hljs-number">0.005</span>; <span class="hljs-comment">// Even slower around Y</span>

    renderer.render(scene, camera);
}

animate();
</code></pre>
<p><strong>Important</strong>: When you increment rotation values in an animation loop (<code>+=</code>), the values accumulate every frame. This creates continuous spinning motion.</p>
<h2 id="heading-scale-resizing-objects">Scale: Resizing Objects</h2>
<p>Scale controls the size of an object along each axis. A scale of <code>1</code> is the original size, <code>2</code> doubles the size, and <code>0.5</code> halves it.</p>
<h3 id="heading-scaling-a-mesh">Scaling a Mesh</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Scale uniformly (all axes the same)</span>
cube.scale.set(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>);  <span class="hljs-comment">// Double the size</span>

<span class="hljs-comment">// Or scale non-uniformly (different on each axis)</span>
cube.scale.set(<span class="hljs-number">1.5</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0.5</span>);  <span class="hljs-comment">// Wider, same height, thinner</span>

<span class="hljs-comment">// Individual axis scaling</span>
cube.scale.x = <span class="hljs-number">1.5</span>;  <span class="hljs-comment">// 150% width</span>
cube.scale.y = <span class="hljs-number">1.0</span>;  <span class="hljs-comment">// Original height</span>
cube.scale.z = <span class="hljs-number">0.5</span>;  <span class="hljs-comment">// 50% depth</span>
</code></pre>
<p>Non-uniform scaling (different values on different axes) is useful for creating stretched or squashed effects, like making a sphere into an egg shape.</p>
<h2 id="heading-complete-basic-example">Complete Basic Example</h2>
<p>Here's a complete example demonstrating all three transformations:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#canvas"</span>);

<span class="hljs-comment">// Scene</span>
<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();

<span class="hljs-comment">// Camera</span>
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(
    <span class="hljs-number">75</span>,
    <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight,
    <span class="hljs-number">0.1</span>,
    <span class="hljs-number">1000</span>
);
camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>);

<span class="hljs-comment">// Renderer</span>
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ canvas });
renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);

<span class="hljs-comment">// Lighting</span>
<span class="hljs-keyword">const</span> ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.5</span>);
scene.add(ambientLight);

<span class="hljs-keyword">const</span> light = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">5</span>);
light.position.set(<span class="hljs-number">5</span>, <span class="hljs-number">5</span>, <span class="hljs-number">5</span>);
scene.add(light);

<span class="hljs-comment">// Geometry and material</span>
<span class="hljs-keyword">const</span> boxGeometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
<span class="hljs-keyword">const</span> material = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0x44aa88</span>,
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0x112244</span>,
});

<span class="hljs-comment">// Create mesh</span>
<span class="hljs-keyword">const</span> cube = <span class="hljs-keyword">new</span> THREE.Mesh(boxGeometry, material);
scene.add(cube);

<span class="hljs-comment">// Apply transformations</span>
cube.position.set(<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>);      <span class="hljs-comment">// Position: right and up</span>
cube.scale.set(<span class="hljs-number">1.5</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0.5</span>);     <span class="hljs-comment">// Scale: wider and thinner</span>
cube.rotation.x = <span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>;   <span class="hljs-comment">// Initial rotation</span>

<span class="hljs-comment">// Animation loop</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
    requestAnimationFrame(animate);

    <span class="hljs-comment">// Animate rotation</span>
    cube.rotation.x += <span class="hljs-number">0.01</span>;
    cube.rotation.y += <span class="hljs-number">0.005</span>;

    renderer.render(scene, camera);
}

animate();

<span class="hljs-comment">// Handle resize</span>
<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"resize"</span>, <span class="hljs-function">() =&gt;</span> {
    camera.aspect = <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);
});
</code></pre>
<h2 id="heading-local-space-vs-world-space">Local Space vs World Space</h2>
<p>One of the most important concepts in 3D graphics is understanding the difference between local and world space.</p>
<ul>
<li><p><strong>Local Space</strong>: Coordinates relative to the object itself</p>
</li>
<li><p><strong>World Space</strong>: Coordinates relative to the entire scene</p>
</li>
</ul>
<h3 id="heading-the-concept">The Concept</h3>
<p>Imagine you're sitting in a moving car. If you move your hand 1 foot to the right (in the car), that movement is in <strong>local space</strong> - relative to the car. But the car itself is moving down the road in <strong>world space</strong>. Your hand's actual position in the world is the combination of both movements.</p>
<h3 id="heading-parent-child-relationships">Parent-Child Relationships</h3>
<p>When you add an object as a child of another object, the child's transformations become relative to its parent.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Parent cube (red)</span>
<span class="hljs-keyword">const</span> parent = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>),
    <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0xff0000</span> })
);
scene.add(parent);

<span class="hljs-comment">// Child cube (blue)</span>
<span class="hljs-keyword">const</span> child = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">0.5</span>, <span class="hljs-number">0.5</span>, <span class="hljs-number">0.5</span>),
    <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0x0000ff</span> })
);

<span class="hljs-comment">// Add child to parent (not to scene!)</span>
parent.add(child);

<span class="hljs-comment">// Child position is relative to parent</span>
child.position.x = <span class="hljs-number">2</span>;  <span class="hljs-comment">// 2 units to the right of parent</span>

<span class="hljs-comment">// Move parent in world space</span>
parent.position.x = <span class="hljs-number">-2</span>;  <span class="hljs-comment">// 2 units to the left in world</span>

<span class="hljs-comment">// Animate</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
    requestAnimationFrame(animate);

    <span class="hljs-comment">// Rotate parent - child rotates with it!</span>
    parent.rotation.y += <span class="hljs-number">0.01</span>;

    renderer.render(scene, camera);
}

animate();
</code></pre>
<h3 id="heading-whats-happening">What's Happening</h3>
<ul>
<li><p>The blue cube is 2 units to the right of the red cube (local space)</p>
</li>
<li><p>The red cube is at position (-2, 0, 0) in world space</p>
</li>
<li><p>The blue cube's world position is automatically calculated as (0, 0, 0)</p>
</li>
<li><p>When the parent rotates, the child rotates around the parent's center</p>
</li>
</ul>
<h3 id="heading-getting-world-position">Getting World Position</h3>
<p>Sometimes you need to know an object's actual position in world space:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> worldPosition = <span class="hljs-keyword">new</span> THREE.Vector3();
child.getWorldPosition(worldPosition);
<span class="hljs-built_in">console</span>.log(worldPosition);  <span class="hljs-comment">// Actual position in world space</span>
</code></pre>
<p>This is useful for collision detection, distance calculations, or positioning effects relative to world coordinates.</p>
<h2 id="heading-groups-organizing-multiple-objects">Groups: Organizing Multiple Objects</h2>
<p>Groups allow you to treat multiple objects as a single unit. This is essential for building complex scenes with logical hierarchies.</p>
<h3 id="heading-why-use-groups">Why Use Groups?</h3>
<ul>
<li><p>Organize related objects (like parts of a character or vehicle)</p>
</li>
<li><p>Transform multiple objects together</p>
</li>
<li><p>Create hierarchical structures (solar systems, robots, etc.)</p>
</li>
<li><p>Simplify scene management</p>
</li>
</ul>
<h3 id="heading-creating-and-using-groups">Creating and Using Groups</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Create a group</span>
<span class="hljs-keyword">const</span> group = <span class="hljs-keyword">new</span> THREE.Group();
scene.add(group);

<span class="hljs-comment">// Create first cube (green)</span>
<span class="hljs-keyword">const</span> cube1 = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>),
    <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0x00ff00</span> })
);
cube1.position.x = <span class="hljs-number">-2</span>;  <span class="hljs-comment">// Position relative to group</span>
group.add(cube1);

<span class="hljs-comment">// Create second cube (blue)</span>
<span class="hljs-keyword">const</span> cube2 = <span class="hljs-keyword">new</span> THREE.Mesh(
    <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>),
    <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ <span class="hljs-attr">color</span>: <span class="hljs-number">0x0000ff</span> })
);
cube2.position.x = <span class="hljs-number">2</span>;  <span class="hljs-comment">// Position relative to group</span>
group.add(cube2);

<span class="hljs-comment">// Transform the entire group</span>
group.position.z = <span class="hljs-number">-3</span>;  <span class="hljs-comment">// Move both cubes back</span>
group.rotation.y = <span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>;  <span class="hljs-comment">// Rotate both cubes</span>

<span class="hljs-comment">// Animate</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
    requestAnimationFrame(animate);

    <span class="hljs-comment">// Rotating the group rotates both cubes around the group's center</span>
    group.rotation.y += <span class="hljs-number">0.01</span>;

    renderer.render(scene, camera);
}

animate();
</code></pre>
<h3 id="heading-hierarchical-transformation">Hierarchical Transformation</h3>
<p>The power of groups comes from hierarchical transformations:</p>
<ol>
<li><p><strong>Child transforms happen first</strong> - Each cube maintains its local position</p>
</li>
<li><p><strong>Parent transforms apply afterward</strong> - The group's rotation affects both cubes</p>
</li>
<li><p><strong>The result is combined</strong> - Both cubes rotate around the group's center while maintaining their spacing</p>
</li>
</ol>
<p>This is how complex objects like characters, vehicles, and mechanical systems are built. For example, a car might have:</p>
<ul>
<li><p>Car body (group)</p>
<ul>
<li><p>Wheels (children of body)</p>
<ul>
<li>Hubcaps (children of wheels)</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>When the car moves, everything moves. When a wheel rotates, only that wheel and its hubcap rotate.</p>
<h2 id="heading-when-to-use-each-technique">When to Use Each Technique</h2>
<p><strong>Direct Transformations</strong> (position, rotation, scale):</p>
<ul>
<li><p>Simple animations</p>
</li>
<li><p>Independent objects</p>
</li>
<li><p>Single mesh manipulation</p>
</li>
</ul>
<p><strong>Parent-Child Relationships</strong>:</p>
<ul>
<li><p>Objects that move together</p>
</li>
<li><p>One object attached to another</p>
</li>
<li><p>Hierarchical structures</p>
</li>
</ul>
<p><strong>Groups</strong>:</p>
<ul>
<li><p>Multiple related objects</p>
</li>
<li><p>Complex scenes with many parts</p>
</li>
<li><p>Logical organization of scene elements</p>
</li>
</ul>
<h2 id="heading-what-i-learned">What I Learned</h2>
<h3 id="heading-1-transformations-are-always-relative">1. Transformations Are Always Relative</h3>
<p>Position, rotation, and scale are relative to an object's parent (or the scene if it has no parent). This makes hierarchical structures powerful but requires understanding the coordinate system.</p>
<h3 id="heading-2-transform-order-matters">2. Transform Order Matters</h3>
<p>Three.js applies transforms in a fixed order: scale → rotation → position. You can't change this, so plan your transformations accordingly.</p>
<h3 id="heading-3-radians-not-degrees">3. Radians, Not Degrees</h3>
<p>Rotation uses radians. Remember: <code>Math.PI</code> is 180°, <code>Math.PI * 2</code> is 360°. Converting from degrees: <code>degrees * (Math.PI / 180)</code>.</p>
<h3 id="heading-4-local-vs-world-space-is-fundamental">4. Local vs World Space Is Fundamental</h3>
<p>Understanding the difference between local and world space is crucial for complex scenes. Use <code>getWorldPosition()</code> when you need absolute coordinates.</p>
<h3 id="heading-5-groups-simplify-complex-scenes">5. Groups Simplify Complex Scenes</h3>
<p>Instead of manually calculating relative positions for multiple objects, use groups to create logical hierarchies. This makes your code cleaner and more maintainable.</p>
<h3 id="heading-6-accumulating-transforms-creates-animation">6. Accumulating Transforms Creates Animation</h3>
<p>Using <code>+=</code> on rotation or position values in an animation loop creates continuous movement. Using <code>=</code> sets absolute values.</p>
<h3 id="heading-7-non-uniform-scaling-is-powerful">7. Non-Uniform Scaling Is Powerful</h3>
<p>Scaling different amounts on different axes creates interesting effects. A sphere scaled as (1, 2, 1) becomes an egg shape.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Transformations are the building blocks of all 3D movement and animation. Mastering position, rotation, and scale - along with understanding local vs world space and hierarchical transformations - gives you complete control over your 3D scenes.</p>
<p>Start with simple transformations on individual objects, then experiment with parent-child relationships and groups. Try building a simple solar system (sun with orbiting planets) or a simple character (body with rotating arms) to practice these concepts.</p>
<p>The key is understanding how transforms combine and propagate through hierarchies. Once this clicks, you can build complex, animated 3D structures with ease.</p>
<p>Keep experimenting!</p>
]]></content:encoded></item><item><title><![CDATA[Mastering Three.js Lighting: Illuminating Your 3D World]]></title><description><![CDATA[Welcome back! 👋 In my previous posts, I covered Three.js basics, geometries and materials. Today, I'm diving into lighting - one of the most important aspects of creating realistic 3D scenes.
Good lighting can make or break your 3D scene. It's the d...]]></description><link>https://blog.iamdipankarpaul.com/mastering-threejs-lighting-illuminating-your-3d-world</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/mastering-threejs-lighting-illuminating-your-3d-world</guid><category><![CDATA[ThreeJS]]></category><category><![CDATA[React]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Tue, 06 Jan 2026 18:30:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767460347128/7a981c34-ebba-464f-9f61-9bf4f1bc1002.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome back! 👋 In my previous posts, I covered <a target="_blank" href="https://blog.iamdipankarpaul.com/my-first-threejs-scene-building-a-3d-world-in-the-browser">Three.js basics</a>, <a target="_blank" href="https://blog.iamdipankarpaul.com/exploring-threejs-geometries-the-building-blocks-of-3d">geometries</a> and <a target="_blank" href="https://claude.ai/chat/e9ad9ae7-a4d2-4a7b-928b-b12582289e7b#">materials</a>. Today, I'm diving into <strong>lighting</strong> - one of the most important aspects of creating realistic 3D scenes.</p>
<p>Good lighting can make or break your 3D scene. It's the difference between a flat, lifeless render and a vibrant, realistic world. Let's explore how to light our scenes like a professional photographer!</p>
<h2 id="heading-what-is-lighting-in-threejs">What is Lighting in Three.js?</h2>
<p>Lighting in Three.js simulates how light behaves in the real world. Different light types mimic different real-world light sources:</p>
<ul>
<li><p><strong>Sun</strong> (DirectionalLight)</p>
</li>
<li><p><strong>Light bulbs</strong> (PointLight)</p>
</li>
<li><p><strong>Flashlights</strong> (SpotLight)</p>
</li>
<li><p><strong>Sky lighting</strong> (HemisphereLight)</p>
</li>
<li><p><strong>Overall brightness</strong> (AmbientLight)</p>
</li>
</ul>
<p>Understanding when and how to use each light type is key to creating the mood and atmosphere you want.</p>
<h2 id="heading-the-photography-analogy">The Photography Analogy</h2>
<p>Think of Three.js lighting is like setting up a photography or film studio:</p>
<ul>
<li><p><strong>Key Light</strong> (main light source) - DirectionalLight or SpotLight</p>
</li>
<li><p><strong>Fill Light</strong> (softens shadows) - AmbientLight or HemisphereLight</p>
</li>
<li><p><strong>Rim/Back Light</strong> (separates subject from background) - PointLight or SpotLight</p>
</li>
<li><p><strong>Practical Lights</strong> (visible light sources) - PointLight with visible geometry</p>
</li>
</ul>
<p>Just like in photography, you rarely use just one light. The magic happens when you combine multiple lights!</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>Here's our HTML structure with controls:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Three.js Lighting Guide<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
        <span class="hljs-selector-tag">body</span> { 
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>; 
            <span class="hljs-attribute">overflow</span>: hidden;
            <span class="hljs-attribute">font-family</span>: Arial, sans-serif;
        }
        <span class="hljs-selector-tag">canvas</span> { 
            <span class="hljs-attribute">display</span>: block; 
        }
        <span class="hljs-selector-id">#controls</span> {
            <span class="hljs-attribute">position</span>: absolute;
            <span class="hljs-attribute">top</span>: <span class="hljs-number">20px</span>;
            <span class="hljs-attribute">right</span>: <span class="hljs-number">20px</span>;
            <span class="hljs-attribute">color</span>: white;
            <span class="hljs-attribute">background</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.85</span>);
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
            <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
            <span class="hljs-attribute">font-size</span>: <span class="hljs-number">13px</span>;
            <span class="hljs-attribute">max-width</span>: <span class="hljs-number">280px</span>;
            <span class="hljs-attribute">max-height</span>: <span class="hljs-number">90vh</span>;
            <span class="hljs-attribute">overflow-y</span>: auto;
        }
        <span class="hljs-selector-id">#controls</span> <span class="hljs-selector-tag">h3</span> {
            <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">0</span>;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#4ecdc4</span>;
            <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
        }
        <span class="hljs-selector-class">.light-section</span> {
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">15px</span> <span class="hljs-number">0</span>;
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
            <span class="hljs-attribute">background</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">0.05</span>);
            <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;
        }
        <span class="hljs-selector-class">.light-section</span> <span class="hljs-selector-tag">h4</span> {
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">10px</span> <span class="hljs-number">0</span>;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#ffe66d</span>;
            <span class="hljs-attribute">font-size</span>: <span class="hljs-number">14px</span>;
        }
        <span class="hljs-selector-class">.control-group</span> {
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">8px</span> <span class="hljs-number">0</span>;
        }
        <span class="hljs-selector-class">.control-group</span> <span class="hljs-selector-tag">label</span> {
            <span class="hljs-attribute">display</span>: block;
            <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">3px</span>;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#aaa</span>;
            <span class="hljs-attribute">font-size</span>: <span class="hljs-number">11px</span>;
        }
        <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"range"</span>]</span> {
            <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
        }
        <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"checkbox"</span>]</span> {
            <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">5px</span>;
        }
        <span class="hljs-selector-class">.toggle-label</span> {
            <span class="hljs-attribute">display</span>: flex;
            <span class="hljs-attribute">align-items</span>: center;
            <span class="hljs-attribute">cursor</span>: pointer;
        }
        <span class="hljs-selector-id">#info</span> {
            <span class="hljs-attribute">position</span>: absolute;
            <span class="hljs-attribute">top</span>: <span class="hljs-number">20px</span>;
            <span class="hljs-attribute">left</span>: <span class="hljs-number">20px</span>;
            <span class="hljs-attribute">color</span>: white;
            <span class="hljs-attribute">background</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.85</span>);
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">15px</span>;
            <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
            <span class="hljs-attribute">font-size</span>: <span class="hljs-number">13px</span>;
            <span class="hljs-attribute">max-width</span>: <span class="hljs-number">250px</span>;
        }
        <span class="hljs-selector-id">#info</span> <span class="hljs-selector-tag">h3</span> {
            <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">0</span>;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#4ecdc4</span>;
        }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"canvas"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"info"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Lighting Demo<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Toggle different lights to see how they affect the scene. Light helpers show light positions.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"controls"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Light Controls<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"light-section"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>Ambient Light<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"toggle-label"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"ambientToggle"</span> <span class="hljs-attr">checked</span>&gt;</span>
                    Enable
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Intensity: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"ambientIntensity"</span>&gt;</span>0.3<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"range"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"ambientSlider"</span> <span class="hljs-attr">min</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">max</span>=<span class="hljs-string">"1"</span> <span class="hljs-attr">step</span>=<span class="hljs-string">"0.1"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"0.3"</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"light-section"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>Directional Light<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"toggle-label"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"directionalToggle"</span> <span class="hljs-attr">checked</span>&gt;</span>
                    Enable
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Intensity: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"directionalIntensity"</span>&gt;</span>1.0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"range"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"directionalSlider"</span> <span class="hljs-attr">min</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">max</span>=<span class="hljs-string">"2"</span> <span class="hljs-attr">step</span>=<span class="hljs-string">"0.1"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"1"</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"light-section"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>Point Light<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"toggle-label"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"pointToggle"</span> <span class="hljs-attr">checked</span>&gt;</span>
                    Enable
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Intensity: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"pointIntensity"</span>&gt;</span>1.0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"range"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"pointSlider"</span> <span class="hljs-attr">min</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">max</span>=<span class="hljs-string">"2"</span> <span class="hljs-attr">step</span>=<span class="hljs-string">"0.1"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"1"</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"light-section"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>Spot Light<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"toggle-label"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"spotToggle"</span> <span class="hljs-attr">checked</span>&gt;</span>
                    Enable
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Intensity: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"spotIntensity"</span>&gt;</span>1.0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"range"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"spotSlider"</span> <span class="hljs-attr">min</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">max</span>=<span class="hljs-string">"2"</span> <span class="hljs-attr">step</span>=<span class="hljs-string">"0.1"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"1"</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Angle: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"spotAngle"</span>&gt;</span>0.5<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"range"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"spotAngleSlider"</span> <span class="hljs-attr">min</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">max</span>=<span class="hljs-string">"1.57"</span> <span class="hljs-attr">step</span>=<span class="hljs-string">"0.1"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"0.5"</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"light-section"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>Hemisphere Light<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"toggle-label"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hemisphereToggle"</span>&gt;</span>
                    Enable
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Intensity: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hemisphereIntensity"</span>&gt;</span>0.5<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"range"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hemisphereSlider"</span> <span class="hljs-attr">min</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">max</span>=<span class="hljs-string">"1"</span> <span class="hljs-attr">step</span>=<span class="hljs-string">"0.1"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"0.5"</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"light-section"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>Helpers<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"toggle-label"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"helpersToggle"</span> <span class="hljs-attr">checked</span>&gt;</span>
                    Show Light Helpers
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/src/lighting.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h2 id="heading-building-our-lighting-scene">Building Our Lighting Scene</h2>
<p>Let's create a scene that demonstrates most used light types!</p>
<h3 id="heading-step-1-scene-and-camera-setup">Step 1: Scene and Camera Setup</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"canvas"</span>);

<span class="hljs-comment">// Scene setup</span>
<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
scene.background = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-number">0x0a0a0a</span>); <span class="hljs-comment">// Very dark background</span>

<span class="hljs-comment">// Camera setup</span>
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(
    <span class="hljs-number">75</span>,
    <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight,
    <span class="hljs-number">0.1</span>,
    <span class="hljs-number">1000</span>
);
camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">8</span>, <span class="hljs-number">20</span>);
camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
</code></pre>
<h3 id="heading-step-2-creating-scene-objects">Step 2: Creating Scene Objects</h3>
<p>Let's create a simple scene with objects that will show off the lighting:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Create a ground plane</span>
<span class="hljs-keyword">const</span> groundGeometry = <span class="hljs-keyword">new</span> THREE.PlaneGeometry(<span class="hljs-number">50</span>, <span class="hljs-number">50</span>);
<span class="hljs-keyword">const</span> groundMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
  <span class="hljs-attr">color</span>: <span class="hljs-number">0x333333</span>,
  <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.8</span>,
  <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.2</span>,
});
<span class="hljs-keyword">const</span> ground = <span class="hljs-keyword">new</span> THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">2</span>; <span class="hljs-comment">// Rotate to be horizontal</span>
ground.position.y = <span class="hljs-number">-2</span>;
scene.add(ground);

<span class="hljs-comment">// Create center sphere</span>
<span class="hljs-keyword">const</span> sphereGeometry = <span class="hljs-keyword">new</span> THREE.SphereGeometry(<span class="hljs-number">2</span>, <span class="hljs-number">64</span>, <span class="hljs-number">64</span>);
<span class="hljs-keyword">const</span> sphereMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
  <span class="hljs-attr">color</span>: <span class="hljs-number">0xffffff</span>,
  <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.3</span>,
  <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.7</span>,
});
<span class="hljs-keyword">const</span> sphere = <span class="hljs-keyword">new</span> THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
scene.add(sphere);

<span class="hljs-comment">// Create surrounding cubes to show light distribution</span>
<span class="hljs-keyword">const</span> cubeGeometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">1.5</span>, <span class="hljs-number">1.5</span>, <span class="hljs-number">1.5</span>);
<span class="hljs-keyword">const</span> cubeMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
  <span class="hljs-attr">color</span>: <span class="hljs-number">0x4ecdc4</span>,
  <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.5</span>,
  <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.3</span>,
});

<span class="hljs-keyword">const</span> cubes = [];
<span class="hljs-keyword">const</span> cubePositions = [
  [<span class="hljs-number">-6</span>, <span class="hljs-number">-0.5</span>, <span class="hljs-number">0</span>], <span class="hljs-comment">// Left</span>
  [<span class="hljs-number">6</span>, <span class="hljs-number">-0.5</span>, <span class="hljs-number">0</span>], <span class="hljs-comment">// Right</span>
  [<span class="hljs-number">0</span>, <span class="hljs-number">-0.5</span>, <span class="hljs-number">6</span>], <span class="hljs-comment">// Front</span>
];

cubePositions.forEach(<span class="hljs-function">(<span class="hljs-params">pos</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> cube = <span class="hljs-keyword">new</span> THREE.Mesh(cubeGeometry, cubeMaterial);
  cube.position.set(pos[<span class="hljs-number">0</span>], pos[<span class="hljs-number">1</span>], pos[<span class="hljs-number">2</span>]);
  cubes.push(cube);
  scene.add(cube);
});

<span class="hljs-comment">// Add a torus for variety</span>
<span class="hljs-keyword">const</span> torusGeometry = <span class="hljs-keyword">new</span> THREE.TorusGeometry(<span class="hljs-number">1.5</span>, <span class="hljs-number">0.5</span>, <span class="hljs-number">16</span>, <span class="hljs-number">100</span>);
<span class="hljs-keyword">const</span> torusMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
  <span class="hljs-attr">color</span>: <span class="hljs-number">0xff6b6b</span>,
  <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.4</span>,
  <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.6</span>,
});
<span class="hljs-keyword">const</span> torus = <span class="hljs-keyword">new</span> THREE.Mesh(torusGeometry, torusMaterial);
torus.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">5</span>, <span class="hljs-number">0</span>);
scene.add(torus);
</code></pre>
<h3 id="heading-step-3-creating-all-light-types">Step 3: Creating All Light Types</h3>
<p>Now let's create each of the six light types with their helpers!</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ============================================</span>
<span class="hljs-comment">// 1. AMBIENT LIGHT</span>
<span class="hljs-comment">// Provides overall base illumination, no direction</span>
<span class="hljs-comment">// Does not cast shadows</span>
<span class="hljs-comment">// Use case: Base lighting so nothing is completely black.</span>
<span class="hljs-comment">// Never use alone, combine with directional lights.</span>
<span class="hljs-comment">// Performance: Very cheap, no calculations needed.</span>

<span class="hljs-comment">// No helper for ambient light (it's everywhere equally)</span>
<span class="hljs-comment">// ============================================</span>
<span class="hljs-keyword">const</span> ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight(
  <span class="hljs-number">0xffffff</span>, <span class="hljs-comment">// White light</span>
  <span class="hljs-number">0.3</span> <span class="hljs-comment">// Low intensity</span>
);
scene.add(ambientLight);

<span class="hljs-comment">// ============================================</span>
<span class="hljs-comment">// 2. DIRECTIONAL LIGHT</span>
<span class="hljs-comment">// Parallel rays like sunlight, illuminates from a direction</span>
<span class="hljs-comment">// Use case: Outdoor scenes (sun/moon), main key light.</span>
<span class="hljs-comment">// All rays are parallel regardless of distance.</span>
<span class="hljs-comment">// Performance: Cheap, good for primary light source.</span>
<span class="hljs-comment">// ============================================</span>
<span class="hljs-keyword">const</span> directionalLight = <span class="hljs-keyword">new</span> THREE.DirectionalLight(
  <span class="hljs-number">0xffffff</span>, <span class="hljs-comment">// White light</span>
  <span class="hljs-number">1.0</span> <span class="hljs-comment">// Full intensity</span>
);
directionalLight.position.set(<span class="hljs-number">5</span>, <span class="hljs-number">10</span>, <span class="hljs-number">5</span>);
scene.add(directionalLight);

<span class="hljs-comment">// Helper to visualize light direction</span>
<span class="hljs-keyword">const</span> directionalHelper = <span class="hljs-keyword">new</span> THREE.DirectionalLightHelper(
  directionalLight,
  <span class="hljs-number">2</span> <span class="hljs-comment">// Helper size</span>
);
scene.add(directionalHelper);

<span class="hljs-comment">// ============================================</span>
<span class="hljs-comment">// 3. POINT LIGHT</span>
<span class="hljs-comment">// Radiates light in all directions from a point (like a light bulb)</span>
<span class="hljs-comment">// Use case: Light bulbs, lamps, candles, explosions.</span>
<span class="hljs-comment">// Light diminishes with distance (uses distance and decay).</span>
<span class="hljs-comment">// Performance: Medium cost, use sparingly for many lights.</span>
<span class="hljs-comment">// ============================================</span>
<span class="hljs-keyword">const</span> pointLight = <span class="hljs-keyword">new</span> THREE.PointLight(
  <span class="hljs-number">0xff00ff</span>, <span class="hljs-comment">// Magenta color</span>
  <span class="hljs-number">1.0</span>, <span class="hljs-comment">// Intensity</span>
  <span class="hljs-number">50</span>, <span class="hljs-comment">// Distance (light fades to 0 at this distance)</span>
  <span class="hljs-number">2</span> <span class="hljs-comment">// Decay (how fast light fades, 2 = realistic)</span>
);
pointLight.position.set(<span class="hljs-number">-8</span>, <span class="hljs-number">3</span>, <span class="hljs-number">0</span>);
scene.add(pointLight);

<span class="hljs-comment">// Helper to visualize light position and range</span>
<span class="hljs-keyword">const</span> pointHelper = <span class="hljs-keyword">new</span> THREE.PointLightHelper(
  pointLight,
  <span class="hljs-number">0.5</span> <span class="hljs-comment">// Helper sphere size</span>
);
scene.add(pointHelper);

<span class="hljs-comment">// ============================================</span>
<span class="hljs-comment">// 4. SPOT LIGHT</span>
<span class="hljs-comment">// Cone of light from a point (like a flashlight or stage light)</span>
<span class="hljs-comment">// Use case: Flashlights, stage spotlights, car headlights.</span>
<span class="hljs-comment">// Creates dramatic focused lighting with soft edges.</span>
<span class="hljs-comment">// Performance: Most expensive, use carefully.</span>
<span class="hljs-comment">// ============================================</span>
<span class="hljs-keyword">const</span> spotLight = <span class="hljs-keyword">new</span> THREE.SpotLight(
  <span class="hljs-number">0x00ffff</span>, <span class="hljs-comment">// Cyan color</span>
  <span class="hljs-number">1.0</span>, <span class="hljs-comment">// Intensity</span>
  <span class="hljs-number">30</span>, <span class="hljs-comment">// Distance</span>
  <span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">10</span>, <span class="hljs-comment">// Angle (cone width in radians)</span>
  <span class="hljs-number">0.5</span>, <span class="hljs-comment">// Penumbra (softness of edge, 0-1)</span>
  <span class="hljs-number">2</span> <span class="hljs-comment">// Decay</span>
);
spotLight.position.set(<span class="hljs-number">-6</span>, <span class="hljs-number">10</span>, <span class="hljs-number">0</span>);
spotLight.target.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>); <span class="hljs-comment">// Point at center</span>
scene.add(spotLight);
scene.add(spotLight.target); <span class="hljs-comment">// Target must be in scene</span>

<span class="hljs-comment">// Helper to visualize cone</span>
<span class="hljs-keyword">const</span> spotHelper = <span class="hljs-keyword">new</span> THREE.SpotLightHelper(spotLight);
scene.add(spotHelper);

<span class="hljs-comment">// ============================================</span>
<span class="hljs-comment">// 5. HEMISPHERE LIGHT</span>
<span class="hljs-comment">// Sky/ground ambient lighting (different colors from top/bottom)</span>
<span class="hljs-comment">// Use case: Outdoor scenes with sky lighting, subtle ambient variation.</span>
<span class="hljs-comment">// Creates natural-looking ambient from above (sky) and below (ground bounce).</span>
<span class="hljs-comment">// Performance: Cheap, great alternative to plain ambient light.</span>
<span class="hljs-comment">// ============================================</span>
<span class="hljs-keyword">const</span> hemisphereLight = <span class="hljs-keyword">new</span> THREE.HemisphereLight(
  <span class="hljs-number">0x0066ff</span>, <span class="hljs-comment">// Sky color (blue)</span>
  <span class="hljs-number">0xff6600</span>, <span class="hljs-comment">// Ground color (orange)</span>
  <span class="hljs-number">0.5</span> <span class="hljs-comment">// Intensity</span>
);
hemisphereLight.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">10</span>, <span class="hljs-number">0</span>);
scene.add(hemisphereLight);

<span class="hljs-comment">// Helper shows sky and ground colors</span>
<span class="hljs-keyword">const</span> hemisphereHelper = <span class="hljs-keyword">new</span> THREE.HemisphereLightHelper(
  hemisphereLight,
  <span class="hljs-number">2</span> <span class="hljs-comment">// Helper size</span>
);
scene.add(hemisphereHelper);

<span class="hljs-comment">// Store references for toggling</span>
<span class="hljs-keyword">const</span> helpers = {
  <span class="hljs-attr">directional</span>: directionalHelper,
  <span class="hljs-attr">point</span>: pointHelper,
  <span class="hljs-attr">spot</span>: spotHelper,
  <span class="hljs-attr">hemisphere</span>: hemisphereHelper,
};

<span class="hljs-comment">// Initially hide some lights to avoid overwhelming the scene</span>
hemisphereLight.visible = <span class="hljs-literal">false</span>;
hemisphereHelper.visible = <span class="hljs-literal">false</span>;
</code></pre>
<h3 id="heading-step-4-interactive-controls">Step 4: Interactive Controls</h3>
<p>Let's wire up all the controls to adjust lights in real-time:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Ambient Light Controls</span>
<span class="hljs-keyword">const</span> ambientToggle = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"ambientToggle"</span>);
<span class="hljs-keyword">const</span> ambientSlider = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"ambientSlider"</span>);
<span class="hljs-keyword">const</span> ambientIntensityDisplay = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"ambientIntensity"</span>);

ambientToggle.addEventListener(<span class="hljs-string">"change"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  ambientLight.visible = e.target.checked;
});

ambientSlider.addEventListener(<span class="hljs-string">"input"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">parseFloat</span>(e.target.value);
  ambientLight.intensity = value;
  ambientIntensityDisplay.textContent = value.toFixed(<span class="hljs-number">1</span>);
});

<span class="hljs-comment">// Directional Light Controls</span>
<span class="hljs-keyword">const</span> directionalToggle = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"directionalToggle"</span>);
<span class="hljs-keyword">const</span> directionalSlider = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"directionalSlider"</span>);
<span class="hljs-keyword">const</span> directionalIntensityDisplay = <span class="hljs-built_in">document</span>.getElementById(
  <span class="hljs-string">"directionalIntensity"</span>
);

directionalToggle.addEventListener(<span class="hljs-string">"change"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  directionalLight.visible = e.target.checked;
  directionalHelper.visible = e.target.checked;
});

directionalSlider.addEventListener(<span class="hljs-string">"input"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">parseFloat</span>(e.target.value);
  directionalLight.intensity = value;
  directionalIntensityDisplay.textContent = value.toFixed(<span class="hljs-number">1</span>);
});

<span class="hljs-comment">// Point Light Controls</span>
<span class="hljs-keyword">const</span> pointToggle = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"pointToggle"</span>);
<span class="hljs-keyword">const</span> pointSlider = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"pointSlider"</span>);
<span class="hljs-keyword">const</span> pointIntensityDisplay = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"pointIntensity"</span>);

pointToggle.addEventListener(<span class="hljs-string">"change"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  pointLight.visible = e.target.checked;
  pointHelper.visible = e.target.checked;
});

pointSlider.addEventListener(<span class="hljs-string">"input"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">parseFloat</span>(e.target.value);
  pointLight.intensity = value;
  pointIntensityDisplay.textContent = value.toFixed(<span class="hljs-number">1</span>);
});

<span class="hljs-comment">// Spot Light Controls</span>
<span class="hljs-keyword">const</span> spotToggle = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"spotToggle"</span>);
<span class="hljs-keyword">const</span> spotSlider = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"spotSlider"</span>);
<span class="hljs-keyword">const</span> spotAngleSlider = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"spotAngleSlider"</span>);
<span class="hljs-keyword">const</span> spotIntensityDisplay = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"spotIntensity"</span>);
<span class="hljs-keyword">const</span> spotAngleDisplay = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"spotAngle"</span>);

spotToggle.addEventListener(<span class="hljs-string">"change"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  spotLight.visible = e.target.checked;
  spotHelper.visible = e.target.checked;
});

spotSlider.addEventListener(<span class="hljs-string">"input"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">parseFloat</span>(e.target.value);
  spotLight.intensity = value;
  spotIntensityDisplay.textContent = value.toFixed(<span class="hljs-number">1</span>);
});

spotAngleSlider.addEventListener(<span class="hljs-string">"input"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">parseFloat</span>(e.target.value);
  spotLight.angle = value;
  spotAngleDisplay.textContent = value.toFixed(<span class="hljs-number">2</span>);
});

<span class="hljs-comment">// Hemisphere Light Controls</span>
<span class="hljs-keyword">const</span> hemisphereToggle = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"hemisphereToggle"</span>);
<span class="hljs-keyword">const</span> hemisphereSlider = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"hemisphereSlider"</span>);
<span class="hljs-keyword">const</span> hemisphereIntensityDisplay = <span class="hljs-built_in">document</span>.getElementById(
  <span class="hljs-string">"hemisphereIntensity"</span>
);

hemisphereToggle.addEventListener(<span class="hljs-string">"change"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  hemisphereLight.visible = e.target.checked;
  hemisphereHelper.visible = e.target.checked;
});

hemisphereSlider.addEventListener(<span class="hljs-string">"input"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">parseFloat</span>(e.target.value);
  hemisphereLight.intensity = value;
  hemisphereIntensityDisplay.textContent = value.toFixed(<span class="hljs-number">1</span>);
});

<span class="hljs-comment">// Helpers Toggle</span>
<span class="hljs-keyword">const</span> helpersToggle = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"helpersToggle"</span>);
helpersToggle.addEventListener(<span class="hljs-string">"change"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> visible = e.target.checked;
  <span class="hljs-built_in">Object</span>.values(helpers).forEach(<span class="hljs-function">(<span class="hljs-params">helper</span>) =&gt;</span> {
    <span class="hljs-comment">// Only show helper if its light is also visible</span>
    <span class="hljs-keyword">const</span> lightVisible = helper.light ? helper.light.visible : <span class="hljs-literal">true</span>;
    helper.visible = visible &amp;&amp; lightVisible;
  });
});
</code></pre>
<h3 id="heading-step-5-renderer-and-animation">Step 5: Renderer and Animation</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Renderer setup</span>
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({
  <span class="hljs-attr">canvas</span>: canvas,
  <span class="hljs-attr">antialias</span>: <span class="hljs-literal">true</span>,
});
renderer.setPixelRatio(<span class="hljs-built_in">window</span>.devicePixelRatio);
renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);

<span class="hljs-comment">// Animation loop</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
  requestAnimationFrame(animate);

  <span class="hljs-keyword">const</span> time = <span class="hljs-built_in">Date</span>.now() * <span class="hljs-number">0.001</span>;

  <span class="hljs-comment">// Rotate center sphere</span>
  sphere.rotation.y = time * <span class="hljs-number">0.3</span>;

  <span class="hljs-comment">// Rotate torus</span>
  torus.rotation.x = time * <span class="hljs-number">0.5</span>;
  torus.rotation.y = time * <span class="hljs-number">0.05</span>;

  <span class="hljs-comment">// Gently bob cubes up and down</span>
  cubes.forEach(<span class="hljs-function">(<span class="hljs-params">cube, index</span>) =&gt;</span> {
    cube.position.y = <span class="hljs-number">-0.5</span> + <span class="hljs-built_in">Math</span>.sin(time + index) * <span class="hljs-number">0.3</span>;
    cube.rotation.y = time * <span class="hljs-number">0.5</span>;
  });

  <span class="hljs-comment">// Animate point light in a circle</span>
  pointLight.position.x = <span class="hljs-built_in">Math</span>.cos(time * <span class="hljs-number">0.5</span>) * <span class="hljs-number">8</span>;
  pointLight.position.z = <span class="hljs-built_in">Math</span>.sin(time * <span class="hljs-number">0.5</span>) * <span class="hljs-number">8</span>;

  <span class="hljs-comment">// Update helpers</span>
  <span class="hljs-keyword">if</span> (spotHelper.visible) spotHelper.update();
  <span class="hljs-keyword">if</span> (directionalHelper.visible) directionalHelper.update();

  renderer.render(scene, camera);
}

animate();

<span class="hljs-comment">// Handle window resize</span>
<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"resize"</span>, <span class="hljs-function">() =&gt;</span> {
  camera.aspect = <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);
});
</code></pre>
<h2 id="heading-understanding-light-properties">Understanding Light Properties</h2>
<h3 id="heading-common-properties-most-lights">Common Properties (Most Lights)</h3>
<p><strong>Colour</strong> - The colour of the light</p>
<pre><code class="lang-javascript">light.color = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-number">0xff0000</span>); <span class="hljs-comment">// Red light</span>
</code></pre>
<p><strong>intensity</strong> - How bright the light is</p>
<pre><code class="lang-javascript">light.intensity = <span class="hljs-number">1.5</span>; <span class="hljs-comment">// 150% brightness</span>
</code></pre>
<h3 id="heading-distance-based-properties-point-amp-spot-lights">Distance-Based Properties (Point &amp; Spot Lights)</h3>
<p><strong>distance</strong> - Maximum range of the light (0 = infinite)</p>
<pre><code class="lang-javascript">pointLight.distance = <span class="hljs-number">50</span>; <span class="hljs-comment">// Light fades to 0 at 50 units</span>
</code></pre>
<p><strong>decay</strong> - How quickly light diminishes with distance</p>
<pre><code class="lang-javascript">pointLight.decay = <span class="hljs-number">2</span>; <span class="hljs-comment">// Physically accurate (inverse square law)</span>
<span class="hljs-comment">// 1 = linear falloff</span>
<span class="hljs-comment">// 2 = realistic falloff (default)</span>
</code></pre>
<h3 id="heading-spot-light-specific">Spot Light Specific</h3>
<p><strong>angle</strong> - Width of the light cone (in radians)</p>
<pre><code class="lang-javascript">spotLight.angle = <span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">6</span>; <span class="hljs-comment">// 30 degrees</span>
</code></pre>
<p><strong>penumbra</strong> - Softness of the cone edge (0-1)</p>
<pre><code class="lang-javascript">spotLight.penumbra = <span class="hljs-number">0.5</span>; <span class="hljs-comment">// 50% soft edge</span>
<span class="hljs-comment">// 0 = hard edge</span>
<span class="hljs-comment">// 1 = very soft edge</span>
</code></pre>
<p><strong>target</strong> - What the spotlight points at</p>
<pre><code class="lang-javascript">spotLight.target.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
scene.add(spotLight.target); <span class="hljs-comment">// Must add target to scene!</span>
</code></pre>
<h3 id="heading-hemisphere-light-specific">Hemisphere Light Specific</h3>
<p><strong>groundColor</strong> - Colour of light from below</p>
<pre><code class="lang-javascript">hemisphereLight.groundColor = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-number">0x080820</span>);
</code></pre>
<h2 id="heading-when-to-use-each-light-type">When to Use Each Light Type</h2>
<h3 id="heading-ambientlight">AmbientLight</h3>
<p><strong>Performance:</strong> ⚡⚡⚡⚡⚡ (Cheapest)<br /><strong>When to use:</strong></p>
<ul>
<li><p>Base lighting, so objects aren't completely black</p>
</li>
<li><p>Fill light to soften harsh shadows</p>
</li>
<li><p>Quick prototyping before adding complex lighting</p>
</li>
</ul>
<p><strong>Best practices:</strong></p>
<ul>
<li><p>Keep intensity low (0.2-0.4)</p>
</li>
<li><p>Never use as the only light source</p>
</li>
<li><p>Combine with directional lights</p>
</li>
</ul>
<p><strong>Real-world equivalent:</strong> Overcast day, diffused light everywhere</p>
<h3 id="heading-directionallight">DirectionalLight</h3>
<p><strong>Performance:</strong> ⚡⚡⚡⚡ (Very Cheap)<br /><strong>When to use:</strong></p>
<ul>
<li><p>Outdoor scenes (sun or moon)</p>
</li>
<li><p>Main key light in any scene</p>
</li>
<li><p>When you need parallel light rays</p>
</li>
</ul>
<p><strong>Best practices:</strong></p>
<ul>
<li><p>Position far from scene center</p>
</li>
<li><p>Use as primary light source</p>
</li>
<li><p>Great for casting consistent shadows</p>
</li>
</ul>
<p><strong>Real-world equivalent:</strong> Sunlight, moonlight</p>
<h3 id="heading-pointlight">PointLight</h3>
<p><strong>Performance:</strong> ⚡⚡⚡ (Medium)<br /><strong>When to use:</strong></p>
<ul>
<li><p>Light bulbs, lamps, candles</p>
</li>
<li><p>Explosions or fire effects</p>
</li>
<li><p>Small localized light sources</p>
</li>
</ul>
<p><strong>Best practices:</strong></p>
<ul>
<li><p>Set appropriate distance and decay</p>
</li>
<li><p>Don't use too many (expensive with many)</p>
</li>
<li><p>Great for accent lighting</p>
</li>
</ul>
<p><strong>Real-world equivalent:</strong> Light bulb, candle, torch</p>
<h3 id="heading-spotlight">SpotLight</h3>
<p><strong>Performance:</strong> ⚡⚡ (Expensive)<br /><strong>When to use:</strong></p>
<ul>
<li><p>Flashlights, car headlights</p>
</li>
<li><p>Stage lighting, theatrical effects</p>
</li>
<li><p>Drawing attention to specific objects</p>
</li>
</ul>
<p><strong>Best practices:</strong></p>
<ul>
<li><p>Use sparingly (most expensive)</p>
</li>
<li><p>Adjust angle and penumbra for desired effect</p>
</li>
<li><p>Remember to add target to scene</p>
</li>
</ul>
<p><strong>Real-world equivalent:</strong> Flashlight, stage spotlight, car headlight</p>
<h3 id="heading-hemispherelight">HemisphereLight</h3>
<p><strong>Performance:</strong> ⚡⚡⚡⚡ (Cheap)<br /><strong>When to use:</strong></p>
<ul>
<li><p>Outdoor scenes with sky</p>
</li>
<li><p>Natural-looking ambient lighting</p>
</li>
<li><p>When you want colour variation (sky vs ground)</p>
</li>
</ul>
<p><strong>Best practices:</strong></p>
<ul>
<li><p>Better than plain Ambient Light for realism</p>
</li>
<li><p>Use sky and ground colours that make sense</p>
</li>
<li><p>Great for outdoor environments</p>
</li>
</ul>
<p><strong>Real-world equivalent:</strong> Natural sky lighting with ground bounce</p>
<h3 id="heading-rectarealight">RectAreaLight</h3>
<p><strong>Performance:</strong> ⚡ (Most Expensive)<br /><strong>When to use:</strong></p>
<ul>
<li><p>Windows or doors letting in light</p>
</li>
<li><p>TV screens, monitors, LED panels</p>
</li>
<li><p>Soft box photography lighting</p>
</li>
<li><p>When you need realistic area lighting</p>
</li>
</ul>
<p><strong>Best practices:</strong></p>
<ul>
<li><p>Only works with Standard/Physical materials!</p>
</li>
<li><p>Use very sparingly (heavy performance cost)</p>
</li>
<li><p>Great for hero objects or key scenes</p>
</li>
</ul>
<p><strong>Real-world equivalent:</strong> Window, LED panel, soft box light</p>
<h2 id="heading-performance-tips-and-light-combinations">Performance Tips and Light Combinations</h2>
<h3 id="heading-performance-hierarchy-cheapest-to-most-expensive">Performance Hierarchy (Cheapest to Most Expensive)</h3>
<ol>
<li><p><strong>AmbientLight / HemisphereLight</strong> - No calculations</p>
</li>
<li><p><strong>DirectionalLight</strong> - Simple directional calculations</p>
</li>
<li><p><strong>PointLight</strong> - Distance-based calculations</p>
</li>
<li><p><strong>SpotLight</strong> - Cone + distance calculations</p>
</li>
<li><p><strong>RectAreaLight</strong> - Complex area calculations</p>
</li>
</ol>
<h3 id="heading-recommended-light-combinations">Recommended Light Combinations</h3>
<p><strong>Basic Scene (Best Performance)</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> ambient = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.4</span>);
<span class="hljs-keyword">const</span> directional = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">1.0</span>);
</code></pre>
<p><strong>Outdoor Scene</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> hemisphere = <span class="hljs-keyword">new</span> THREE.HemisphereLight(<span class="hljs-number">0x87CEEB</span>, <span class="hljs-number">0x8B4513</span>, <span class="hljs-number">0.6</span>);
<span class="hljs-keyword">const</span> directional = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.8</span>);
</code></pre>
<p><strong>Indoor Scene</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> ambient = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.3</span>);
<span class="hljs-keyword">const</span> pointLight1 = <span class="hljs-keyword">new</span> THREE.PointLight(<span class="hljs-number">0xffffee</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">20</span>);
<span class="hljs-keyword">const</span> pointLight2 = <span class="hljs-keyword">new</span> THREE.PointLight(<span class="hljs-number">0xffffee</span>, <span class="hljs-number">0.8</span>, <span class="hljs-number">15</span>);
</code></pre>
<p><strong>Dramatic/Cinematic</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> ambient = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.2</span>);
<span class="hljs-keyword">const</span> spot1 = <span class="hljs-keyword">new</span> THREE.SpotLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">1.5</span>); <span class="hljs-comment">// Key light</span>
<span class="hljs-keyword">const</span> spot2 = <span class="hljs-keyword">new</span> THREE.SpotLight(<span class="hljs-number">0x4444ff</span>, <span class="hljs-number">0.5</span>); <span class="hljs-comment">// Rim light</span>
</code></pre>
<p><strong>High-Quality Product Visualization</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> ambient = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.3</span>);
<span class="hljs-keyword">const</span> rectArea = <span class="hljs-keyword">new</span> THREE.RectAreaLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">2.0</span>, <span class="hljs-number">10</span>, <span class="hljs-number">10</span>);
<span class="hljs-keyword">const</span> directional = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.8</span>);
</code></pre>
<h2 id="heading-what-i-learned">What I Learned</h2>
<h3 id="heading-1-lighting-makes-or-breaks-a-scene">1. <strong>Lighting Makes or Breaks a Scene</strong></h3>
<p>The same geometry and materials look entirely different under different lighting. Good lighting is often more important than complex geometry!</p>
<h3 id="heading-2-combine-multiple-lights">2. <strong>Combine Multiple Lights</strong></h3>
<p>Professional scenes rarely use just one light. The "three-point lighting" technique from photography (key, fill, rim) applies to 3D too!</p>
<h3 id="heading-3-light-helpers-are-essential">3. <strong>Light Helpers Are Essential</strong></h3>
<p>Always use light helpers when setting up your scene. They show exactly where lights are positioned and what they're illuminating. Turn them off for final render.</p>
<h3 id="heading-4-performance-matters">4. <strong>Performance Matters</strong></h3>
<p>More lights = worse performance. Start with cheap lights (Ambient, Directional) and add expensive ones (Spot, RectArea) only where needed.</p>
<h3 id="heading-5-distance-and-decay-are-critical">5. <strong>Distance and Decay Are Critical</strong></h3>
<p>For Point and Spot lights, setting appropriate distance and decay values is crucial. A decay of 2 is physically accurate and usually looks best.</p>
<h3 id="heading-6-rectarealight-limitations">6. <strong>RectAreaLight Limitations</strong></h3>
<p>RectAreaLight only works with MeshStandardMaterial and MeshPhysicalMaterial. If your objects look unlit, check your material type!</p>
<h3 id="heading-7-colour-temperature-matters">7. <strong>Colour Temperature Matters</strong></h3>
<p>Real lights have colour! Sunlight is slightly yellow (0xffffee), fluorescent lights are cooler (0xeeeeff), candlelight is warm orange (0xff8844). Adding subtle colour makes scenes more realistic.</p>
<h3 id="heading-8-light-position-is-key">8. <strong>Light Position Is Key</strong></h3>
<p>The same light at different positions creates entirely different moods. Experiment with positioning lights high, low, front, back, and sides.</p>
<h3 id="heading-9-hemisphere-light-is-underrated">9. <strong>Hemisphere Light Is Underrated</strong></h3>
<p>Instead of plain AmbientLight, HemisphereLight adds subtle colour variation from sky and ground that makes outdoor scenes look much more natural.</p>
<h3 id="heading-10-start-simple-then-add">10. <strong>Start Simple, Then Add</strong></h3>
<p>Begin with just ambient + directional light. Once that looks good, add accent lights (point, spot) for drama and interest.</p>
<h2 id="heading-common-lighting-recipes">Common Lighting Recipes</h2>
<p>Here are some lighting setups I found useful:</p>
<h3 id="heading-sunny-day">Sunny Day</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> hemisphere = <span class="hljs-keyword">new</span> THREE.HemisphereLight(<span class="hljs-number">0x87CEEB</span>, <span class="hljs-number">0x8B7355</span>, <span class="hljs-number">0.6</span>);
<span class="hljs-keyword">const</span> sun = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffee</span>, <span class="hljs-number">1.0</span>);
sun.position.set(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>, <span class="hljs-number">10</span>);
</code></pre>
<h3 id="heading-overcast-day">Overcast Day</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> ambient = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xcccccc</span>, <span class="hljs-number">0.8</span>);
<span class="hljs-keyword">const</span> directional = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.3</span>);
</code></pre>
<h3 id="heading-night-scene-with-moon">Night Scene with Moon</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> ambient = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0x111144</span>, <span class="hljs-number">0.2</span>);
<span class="hljs-keyword">const</span> moon = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xaaaaff</span>, <span class="hljs-number">0.5</span>);
moon.position.set(<span class="hljs-number">-10</span>, <span class="hljs-number">20</span>, <span class="hljs-number">5</span>);
</code></pre>
<h3 id="heading-indoor-warm-lighting">Indoor Warm Lighting</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> ambient = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0x332211</span>, <span class="hljs-number">0.3</span>);
<span class="hljs-keyword">const</span> lamp1 = <span class="hljs-keyword">new</span> THREE.PointLight(<span class="hljs-number">0xffaa66</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">15</span>, <span class="hljs-number">2</span>);
<span class="hljs-keyword">const</span> lamp2 = <span class="hljs-keyword">new</span> THREE.PointLight(<span class="hljs-number">0xffaa66</span>, <span class="hljs-number">0.8</span>, <span class="hljs-number">12</span>, <span class="hljs-number">2</span>);
</code></pre>
<h3 id="heading-studiogallery-lighting">Studio/Gallery Lighting</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> ambient = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.4</span>);
<span class="hljs-keyword">const</span> key = <span class="hljs-keyword">new</span> THREE.SpotLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">1.5</span>, <span class="hljs-number">30</span>, <span class="hljs-built_in">Math</span>.PI/<span class="hljs-number">6</span>, <span class="hljs-number">0.3</span>);
<span class="hljs-keyword">const</span> fill = <span class="hljs-keyword">new</span> THREE.SpotLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.5</span>, <span class="hljs-number">25</span>, <span class="hljs-built_in">Math</span>.PI/<span class="hljs-number">4</span>, <span class="hljs-number">0.5</span>);
<span class="hljs-keyword">const</span> rim = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0x8888ff</span>, <span class="hljs-number">0.6</span>);
</code></pre>
<h2 id="heading-debugging-lighting-issues">Debugging Lighting Issues</h2>
<p>If your scene looks wrong, check these:</p>
<p><strong>Objects Are Completely Black</strong></p>
<ul>
<li><p>Are lights added to the scene?</p>
</li>
<li><p>Do materials need lights? (Basic doesn't, others do)</p>
</li>
<li><p>Is ambient light intensity too low?</p>
</li>
</ul>
<p><strong>Lights Don't Seem to Work</strong></p>
<ul>
<li><p>Check light visibility (light.visible = true)</p>
</li>
<li><p>Verify light helpers are showing correct positions</p>
</li>
<li><p>For SpotLight, did you add the target to scene?</p>
</li>
<li><p>For RectAreaLight, are you using Standard/Physical material?</p>
</li>
</ul>
<p><strong>Scene Is Too Dark/Bright</strong></p>
<ul>
<li><p>Adjust ambient light intensity (start around 0.3)</p>
</li>
<li><p>Change directional light intensity (start around 1.0)</p>
</li>
<li><p>Check camera exposure (some renderers support this)</p>
</li>
</ul>
<p><strong>Point/Spot Lights Fade Too Fast</strong></p>
<ul>
<li><p>Increase distance property</p>
</li>
<li><p>Check decay value (2 is realistic but you can adjust)</p>
</li>
<li><p>Increase intensity</p>
</li>
</ul>
<h2 id="heading-next-steps">Next Steps</h2>
<p>Now that I understand lighting, here's what I'm planning to explore:</p>
<ul>
<li><p>Adding shadows to lights for more realism</p>
</li>
<li><p>Using textures on materials</p>
</li>
<li><p>Environment maps for realistic reflections</p>
</li>
<li><p>Post-processing effects (bloom, ambient occlusion)</p>
</li>
<li><p>Combining great lighting with textured materials</p>
</li>
</ul>
<h2 id="heading-try-experimenting-with">Try experimenting with:</h2>
<ul>
<li><p>Different light colour combinations</p>
</li>
<li><p>Three-point lighting setups key (directional), fill (ambient) and rim (spot light)</p>
</li>
<li><p>Animating light positions for dynamic effects</p>
</li>
<li><p>Adjusting intensity to create different moods</p>
</li>
<li><p>Combining lights to simulate real environments</p>
</li>
</ul>
<p>Understanding lighting is a game-changer. Happy illuminating! 💡</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Three.js Materials: From Basic to Physical]]></title><description><![CDATA[Welcome back! 👋 In my previous posts, I covered the basics of Three.js and different geometries. Today, I'm exploring materials - what makes objects look the way they do.
If geometries are the shape of an object, materials are its appearance - the c...]]></description><link>https://blog.iamdipankarpaul.com/understanding-threejs-materials-from-basic-to-physical</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/understanding-threejs-materials-from-basic-to-physical</guid><category><![CDATA[ThreeJS]]></category><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Sun, 04 Jan 2026 18:30:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767454203208/467bb91d-817e-41ed-b500-8deee933aa91.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome back! 👋 In my previous posts, I covered the <a target="_blank" href="https://blog.iamdipankarpaul.com/my-first-threejs-scene-building-a-3d-world-in-the-browser">basics of Three.js</a> and <a target="_blank" href="https://blog.iamdipankarpaul.com/exploring-threejs-geometries-the-building-blocks-of-3d">different geometries</a>. Today, I'm exploring <strong>materials</strong> - what makes objects look the way they do.</p>
<p>If geometries are the shape of an object, materials are its appearance - the colour, shininess, transparency and how it reacts to light. Let's dive in!</p>
<h2 id="heading-what-are-materials">What Are Materials?</h2>
<p>In Three.js, materials define how the surface of a geometry looks and behaves. They control:</p>
<ul>
<li><p><strong>Colour and appearance</strong> (matte, shiny, metallic)</p>
</li>
<li><p><strong>Light interaction</strong> (does it need lights to be visible?)</p>
</li>
<li><p><strong>Transparency</strong> (can you see through it?)</p>
</li>
<li><p><strong>Special effects</strong> (glowing, wireframe mode)</p>
</li>
</ul>
<p>Different materials have different performance costs and visual capabilities. Choosing the right material is about balancing looks and performance.</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>Here's our basic HTML structure:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Three.js Materials Gallery<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
        <span class="hljs-selector-tag">body</span> {
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
            <span class="hljs-attribute">overflow</span>: hidden;
            <span class="hljs-attribute">font-family</span>: Arial, sans-serif;
        }
        <span class="hljs-selector-tag">canvas</span> {
            <span class="hljs-attribute">display</span>: block;
        }
        <span class="hljs-selector-id">#controls</span> {
            <span class="hljs-attribute">position</span>: absolute;
            <span class="hljs-attribute">top</span>: <span class="hljs-number">20px</span>;
            <span class="hljs-attribute">right</span>: <span class="hljs-number">20px</span>;
            <span class="hljs-attribute">color</span>: white;
            <span class="hljs-attribute">background</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.8</span>);
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
            <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
            <span class="hljs-attribute">font-size</span>: <span class="hljs-number">14px</span>;
            <span class="hljs-attribute">max-width</span>: <span class="hljs-number">300px</span>;
        }
        <span class="hljs-selector-id">#controls</span> <span class="hljs-selector-tag">h3</span> {
            <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">0</span>;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#4ecdc4</span>;
        }
        <span class="hljs-selector-class">.control-group</span> {
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">15px</span> <span class="hljs-number">0</span>;
        }
        <span class="hljs-selector-class">.control-group</span> <span class="hljs-selector-tag">label</span> {
            <span class="hljs-attribute">display</span>: block;
            <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">5px</span>;
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#aaa</span>;
        }
        <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"range"</span>]</span> {
            <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
        }
        <span class="hljs-selector-tag">button</span> {
            <span class="hljs-attribute">background</span>: <span class="hljs-number">#4ecdc4</span>;
            <span class="hljs-attribute">color</span>: black;
            <span class="hljs-attribute">border</span>: none;
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">8px</span> <span class="hljs-number">15px</span>;
            <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;
            <span class="hljs-attribute">cursor</span>: pointer;
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">5px</span> <span class="hljs-number">5px</span> <span class="hljs-number">5px</span> <span class="hljs-number">0</span>;
            <span class="hljs-attribute">font-weight</span>: bold;
        }
        <span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:hover</span> {
            <span class="hljs-attribute">background</span>: <span class="hljs-number">#45b8af</span>;
        }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"canvas"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"controls"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Material Properties<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Metalness: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"metalnessValue"</span>&gt;</span>0.5<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"range"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"metalness"</span> <span class="hljs-attr">min</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">max</span>=<span class="hljs-string">"1"</span> <span class="hljs-attr">step</span>=<span class="hljs-string">"0.1"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"0.5"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Roughness: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"roughnessValue"</span>&gt;</span>0.5<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"range"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"roughness"</span> <span class="hljs-attr">min</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">max</span>=<span class="hljs-string">"1"</span> <span class="hljs-attr">step</span>=<span class="hljs-string">"0.1"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"0.5"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Opacity: <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"opacityValue"</span>&gt;</span>1.0<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"range"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"opacity"</span> <span class="hljs-attr">min</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">max</span>=<span class="hljs-string">"1"</span> <span class="hljs-attr">step</span>=<span class="hljs-string">"0.1"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"1"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"control-group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"toggleWireframe"</span>&gt;</span>Toggle Wireframe<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"toggleEmissive"</span>&gt;</span>Toggle Glow<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"script.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h2 id="heading-building-our-materials-gallery">Building Our Materials Gallery</h2>
<p>Let's create a scene with the same sphere shape using five different materials to see how they compare!</p>
<h3 id="heading-step-1-scene-and-camera-setup">Step 1: Scene and Camera Setup</h3>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"canvas"</span>);

<span class="hljs-comment">// Scene setup</span>
<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
scene.background = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-number">0x1a1a2e</span>); <span class="hljs-comment">// Dark blue background</span>

<span class="hljs-comment">// Camera setup</span>
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(
    <span class="hljs-number">75</span>,
    <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight,
    <span class="hljs-number">0.1</span>,
    <span class="hljs-number">1000</span>
);
camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">15</span>); <span class="hljs-comment">// Pull back to see all spheres</span>
camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
</code></pre>
<h3 id="heading-step-2-lighting-setup">Step 2: Lighting Setup</h3>
<pre><code class="lang-jsx"><span class="hljs-comment">// Ambient light - base illumination for all objects</span>
<span class="hljs-keyword">const</span> ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.3</span>);
scene.add(ambientLight);

<span class="hljs-comment">// Directional light - main light source (like sunlight)</span>
<span class="hljs-keyword">const</span> directionalLight = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">1</span>);
directionalLight.position.set(<span class="hljs-number">5</span>, <span class="hljs-number">5</span>, <span class="hljs-number">5</span>);
scene.add(directionalLight);

<span class="hljs-comment">// Point light - adds dynamic colored lighting</span>
<span class="hljs-keyword">const</span> pointLight = <span class="hljs-keyword">new</span> THREE.PointLight(<span class="hljs-number">0xff00ff</span>, <span class="hljs-number">1</span>);
pointLight.position.set(<span class="hljs-number">-5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>);
scene.add(pointLight);

<span class="hljs-comment">// Another point light on the opposite side</span>
<span class="hljs-keyword">const</span> pointLight2 = <span class="hljs-keyword">new</span> THREE.PointLight(<span class="hljs-number">0x00ffff</span>, <span class="hljs-number">0.8</span>);
pointLight2.position.set(<span class="hljs-number">5</span>, <span class="hljs-number">-3</span>, <span class="hljs-number">5</span>);
scene.add(pointLight2);
</code></pre>
<h3 id="heading-step-3-creating-materials">Step 3: Creating Materials</h3>
<p>Now let's create our five different materials!</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// ============================================</span>
<span class="hljs-comment">// 1. MESH BASIC MATERIAL</span>
<span class="hljs-comment">// Simplest material - NO lighting calculation</span>
<span class="hljs-comment">// ============================================</span>
<span class="hljs-keyword">const</span> basicMaterial = <span class="hljs-keyword">new</span> THREE.MeshBasicMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xff6b6b</span>,           <span class="hljs-comment">// Red color</span>
    <span class="hljs-attr">wireframe</span>: <span class="hljs-literal">false</span>           <span class="hljs-comment">// Show as solid (not wireframe)</span>
});

<span class="hljs-comment">// Use case: UI elements, backgrounds, objects that should</span>
<span class="hljs-comment">// always be visible regardless of lighting</span>

<span class="hljs-comment">// ============================================</span>
<span class="hljs-comment">// 2. MESH LAMBERT MATERIAL</span>
<span class="hljs-comment">// Matte (non-shiny) surfaces with lighting</span>
<span class="hljs-comment">// ============================================</span>
<span class="hljs-keyword">const</span> lambertMaterial = <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0x4ecdc4</span>,           <span class="hljs-comment">// Cyan color</span>
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0x000000</span>,        <span class="hljs-comment">// No glow by default</span>
    <span class="hljs-attr">wireframe</span>: <span class="hljs-literal">false</span>
});

<span class="hljs-comment">// Use case: Matte surfaces like paper, unpolished wood,</span>
<span class="hljs-comment">// fabric, chalk. Good performance for non-reflective objects.</span>

<span class="hljs-comment">// ============================================</span>
<span class="hljs-comment">// 3. MESH PHONG MATERIAL</span>
<span class="hljs-comment">// Shiny surfaces with specular highlights</span>
<span class="hljs-comment">// ============================================</span>
<span class="hljs-keyword">const</span> phongMaterial = <span class="hljs-keyword">new</span> THREE.MeshPhongMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xffe66d</span>,           <span class="hljs-comment">// Yellow color</span>
    <span class="hljs-attr">shininess</span>: <span class="hljs-number">100</span>,            <span class="hljs-comment">// How shiny (0-100+)</span>
    <span class="hljs-attr">specular</span>: <span class="hljs-number">0xffffff</span>,        <span class="hljs-comment">// Color of the shine</span>
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0x000000</span>,
    <span class="hljs-attr">wireframe</span>: <span class="hljs-literal">false</span>
});

<span class="hljs-comment">// Use case: Plastic, polished surfaces, glossy paint.</span>
<span class="hljs-comment">// Creates visible light reflections (specular highlights).</span>

<span class="hljs-comment">// ============================================</span>
<span class="hljs-comment">// 4. MESH STANDARD MATERIAL</span>
<span class="hljs-comment">// Physically Based Rendering (PBR) - realistic materials</span>
<span class="hljs-comment">// ============================================</span>
<span class="hljs-keyword">const</span> standardMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xa8e6cf</span>,           <span class="hljs-comment">// Mint green</span>
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.5</span>,            <span class="hljs-comment">// How metallic (0 = non-metal, 1 = full metal)</span>
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.5</span>,            <span class="hljs-comment">// Surface roughness (0 = smooth, 1 = rough)</span>
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0x000000</span>,
    <span class="hljs-attr">wireframe</span>: <span class="hljs-literal">false</span>
});

<span class="hljs-comment">// Use case: Most modern 3D applications. Provides realistic</span>
<span class="hljs-comment">// materials using metalness/roughness workflow. Good for</span>
<span class="hljs-comment">// metals, plastics, and general realistic rendering.</span>

<span class="hljs-comment">// ============================================</span>
<span class="hljs-comment">// 5. MESH PHYSICAL MATERIAL</span>
<span class="hljs-comment">// Advanced PBR with extra features</span>
<span class="hljs-comment">// ============================================</span>
<span class="hljs-keyword">const</span> physicalMaterial = <span class="hljs-keyword">new</span> THREE.MeshPhysicalMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xffa8e8</span>,           <span class="hljs-comment">// Pink</span>
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.8</span>,
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.2</span>,
    <span class="hljs-attr">clearcoat</span>: <span class="hljs-number">1.0</span>,            <span class="hljs-comment">// Clear coating layer (like car paint)</span>
    <span class="hljs-attr">clearcoatRoughness</span>: <span class="hljs-number">0.1</span>,   <span class="hljs-comment">// Roughness of the clear coat</span>
    <span class="hljs-attr">reflectivity</span>: <span class="hljs-number">1.0</span>,         <span class="hljs-comment">// How reflective</span>
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0x000000</span>,
    <span class="hljs-attr">wireframe</span>: <span class="hljs-literal">false</span>
});

<span class="hljs-comment">// Use case: Car paint, gems, high-quality materials that need</span>
<span class="hljs-comment">// extra realism. Most expensive performance-wise but most realistic.</span>
</code></pre>
<h2 id="heading-understanding-material-properties">Understanding Material Properties</h2>
<p>Let me break down the key properties you'll use most often:</p>
<h3 id="heading-common-properties-all-materials">Common Properties (All Materials)</h3>
<p><strong>Colour</strong> - The base colour of the material</p>
<pre><code class="lang-jsx">material.color = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-number">0xff0000</span>); <span class="hljs-comment">// Red</span>
</code></pre>
<p><strong>opacity</strong> - Transparency level (0 = invisible, 1 = solid)</p>
<pre><code class="lang-jsx">material.transparent = <span class="hljs-literal">true</span>; <span class="hljs-comment">// Must enable this first</span>
material.opacity = <span class="hljs-number">0.5</span>; <span class="hljs-comment">// 50% transparent</span>
</code></pre>
<p><strong>wireframe</strong> - Shows the geometry's triangle structure</p>
<pre><code class="lang-jsx">material.wireframe = <span class="hljs-literal">true</span>; <span class="hljs-comment">// Great for debugging!</span>
</code></pre>
<p><strong>visible</strong> - Whether the material renders at all</p>
<pre><code class="lang-jsx">material.visible = <span class="hljs-literal">false</span>; <span class="hljs-comment">// Hide the object</span>
</code></pre>
<h3 id="heading-lighting-based-properties">Lighting-Based Properties</h3>
<p><strong>emissive</strong> - Makes material glow (like it has its own light)</p>
<pre><code class="lang-jsx">material.emissive = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-number">0x00ff00</span>); <span class="hljs-comment">// Green glow</span>
</code></pre>
<p><strong>emissiveIntensity</strong> - How strong the glow is</p>
<pre><code class="lang-jsx">material.emissiveIntensity = <span class="hljs-number">0.5</span>; <span class="hljs-comment">// 50% glow strength</span>
</code></pre>
<h3 id="heading-pbr-properties-standard-amp-physical-materials">PBR Properties (Standard &amp; Physical Materials)</h3>
<p><strong>metalness</strong> - How metallic the surface is (0-1)</p>
<ul>
<li><p>0 = Non-metal (plastic, wood, rubber)</p>
</li>
<li><p>1 = Full metal (gold, steel, copper)</p>
</li>
</ul>
<p><strong>roughness</strong> - How rough/smooth the surface is (0-1)</p>
<ul>
<li><p>0 = Mirror-smooth (polished metal)</p>
</li>
<li><p>1 = Very rough (concrete, rough wood)</p>
</li>
</ul>
<h3 id="heading-physical-material-extras">Physical Material Extras</h3>
<p><strong>clearcoat</strong> - Adds a glossy layer on top (like car paint)</p>
<pre><code class="lang-jsx">material.clearcoat = <span class="hljs-number">1.0</span>; <span class="hljs-comment">// Full clear coat</span>
material.clearcoatRoughness = <span class="hljs-number">0.1</span>; <span class="hljs-comment">// Slightly rough coating</span>
</code></pre>
<p><strong>transmission</strong> - How much light passes through (glass effect)</p>
<pre><code class="lang-jsx">material.transmission = <span class="hljs-number">1.0</span>; <span class="hljs-comment">// Fully transparent to light</span>
</code></pre>
<h3 id="heading-step-4-creating-spheres-with-different-materials">Step 4: Creating Spheres with Different Materials</h3>
<pre><code class="lang-jsx"><span class="hljs-comment">// Create base geometry (we'll reuse this for all spheres)</span>
<span class="hljs-keyword">const</span> sphereGeometry = <span class="hljs-keyword">new</span> THREE.SphereGeometry(<span class="hljs-number">1.5</span>, <span class="hljs-number">64</span>, <span class="hljs-number">64</span>);
<span class="hljs-comment">// Using 64 segments for smooth appearance</span>

<span class="hljs-comment">// Create 5 spheres, each with a different material</span>
<span class="hljs-keyword">const</span> basicSphere = <span class="hljs-keyword">new</span> THREE.Mesh(sphereGeometry, basicMaterial);
basicSphere.position.set(<span class="hljs-number">-8</span>, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>);
scene.add(basicSphere);

<span class="hljs-keyword">const</span> lambertSphere = <span class="hljs-keyword">new</span> THREE.Mesh(sphereGeometry, lambertMaterial);
lambertSphere.position.set(<span class="hljs-number">-4</span>, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>);
scene.add(lambertSphere);

<span class="hljs-keyword">const</span> phongSphere = <span class="hljs-keyword">new</span> THREE.Mesh(sphereGeometry, phongMaterial);
phongSphere.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>);
scene.add(phongSphere);

<span class="hljs-keyword">const</span> standardSphere = <span class="hljs-keyword">new</span> THREE.Mesh(sphereGeometry, standardMaterial);
standardSphere.position.set(<span class="hljs-number">4</span>, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>);
scene.add(standardSphere);

<span class="hljs-keyword">const</span> physicalSphere = <span class="hljs-keyword">new</span> THREE.Mesh(sphereGeometry, physicalMaterial);
physicalSphere.position.set(<span class="hljs-number">8</span>, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>);
scene.add(physicalSphere);

<span class="hljs-comment">// Store references for interactive controls</span>
<span class="hljs-keyword">const</span> spheres = [basicSphere, lambertSphere, phongSphere, standardSphere, physicalSphere];
<span class="hljs-keyword">const</span> materials = [basicMaterial, lambertMaterial, phongMaterial, standardMaterial, physicalMaterial];
</code></pre>
<h3 id="heading-step-5-adding-labels">Step 5: Adding Labels</h3>
<p>Let's add text labels so we know which material is which:</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// Create text labels using canvas textures</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createTextLabel</span>(<span class="hljs-params">text</span>) </span>{
    <span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'canvas'</span>);
    <span class="hljs-keyword">const</span> context = canvas.getContext(<span class="hljs-string">'2d'</span>);
    canvas.width = <span class="hljs-number">512</span>;
    canvas.height = <span class="hljs-number">128</span>;

    context.fillStyle = <span class="hljs-string">'#000000'</span>;
    context.fillRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvas.width, canvas.height);

    context.font = <span class="hljs-string">'Bold 48px Arial'</span>;
    context.fillStyle = <span class="hljs-string">'#ffffff'</span>;
    context.textAlign = <span class="hljs-string">'center'</span>;
    context.fillText(text, canvas.width / <span class="hljs-number">2</span>, canvas.height / <span class="hljs-number">2</span> + <span class="hljs-number">16</span>);

    <span class="hljs-keyword">const</span> texture = <span class="hljs-keyword">new</span> THREE.CanvasTexture(canvas);
    <span class="hljs-keyword">const</span> spriteMaterial = <span class="hljs-keyword">new</span> THREE.SpriteMaterial({ <span class="hljs-attr">map</span>: texture });
    <span class="hljs-keyword">const</span> sprite = <span class="hljs-keyword">new</span> THREE.Sprite(spriteMaterial);
    sprite.scale.set(<span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);

    <span class="hljs-keyword">return</span> sprite;
}

<span class="hljs-comment">// Add labels below each sphere</span>
<span class="hljs-keyword">const</span> labels = [
    { <span class="hljs-attr">text</span>: <span class="hljs-string">'Basic'</span>, <span class="hljs-attr">position</span>: [<span class="hljs-number">-8</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] },
    { <span class="hljs-attr">text</span>: <span class="hljs-string">'Lambert'</span>, <span class="hljs-attr">position</span>: [<span class="hljs-number">-4</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] },
    { <span class="hljs-attr">text</span>: <span class="hljs-string">'Phong'</span>, <span class="hljs-attr">position</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] },
    { <span class="hljs-attr">text</span>: <span class="hljs-string">'Standard'</span>, <span class="hljs-attr">position</span>: [<span class="hljs-number">4</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] },
    { <span class="hljs-attr">text</span>: <span class="hljs-string">'Physical'</span>, <span class="hljs-attr">position</span>: [<span class="hljs-number">8</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] }
];

labels.forEach(<span class="hljs-function"><span class="hljs-params">label</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> sprite = createTextLabel(label.text);
    sprite.position.set(label.position[<span class="hljs-number">0</span>], label.position[<span class="hljs-number">1</span>], label.position[<span class="hljs-number">2</span>]);
    scene.add(sprite);
});
</code></pre>
<h3 id="heading-step-6-interactive-controls">Step 6: Interactive Controls</h3>
<p>Now let's add interactivity so we can adjust material properties in real-time!</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// Get control elements</span>
<span class="hljs-keyword">const</span> metalnessSlider = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'metalness'</span>);
<span class="hljs-keyword">const</span> roughnessSlider = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'roughness'</span>);
<span class="hljs-keyword">const</span> opacitySlider = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'opacity'</span>);
<span class="hljs-keyword">const</span> wireframeBtn = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'toggleWireframe'</span>);
<span class="hljs-keyword">const</span> emissiveBtn = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'toggleEmissive'</span>);

<span class="hljs-keyword">const</span> metalnessValue = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'metalnessValue'</span>);
<span class="hljs-keyword">const</span> roughnessValue = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'roughnessValue'</span>);
<span class="hljs-keyword">const</span> opacityValue = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'opacityValue'</span>);

<span class="hljs-comment">// Metalness control (only affects Standard and Physical materials)</span>
metalnessSlider.addEventListener(<span class="hljs-string">'input'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">parseFloat</span>(e.target.value);
    metalnessValue.textContent = value.toFixed(<span class="hljs-number">1</span>);

    <span class="hljs-comment">// Only Standard and Physical materials have metalness</span>
    standardMaterial.metalness = value;
    physicalMaterial.metalness = value;
});

<span class="hljs-comment">// Roughness control (only affects Standard and Physical materials)</span>
roughnessSlider.addEventListener(<span class="hljs-string">'input'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">parseFloat</span>(e.target.value);
    roughnessValue.textContent = value.toFixed(<span class="hljs-number">1</span>);

    standardMaterial.roughness = value;
    physicalMaterial.roughness = value;
});

<span class="hljs-comment">// Opacity control (affects all materials)</span>
opacitySlider.addEventListener(<span class="hljs-string">'input'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">parseFloat</span>(e.target.value);
    opacityValue.textContent = value.toFixed(<span class="hljs-number">1</span>);

    <span class="hljs-comment">// Enable transparency and set opacity for all materials</span>
    materials.forEach(<span class="hljs-function"><span class="hljs-params">material</span> =&gt;</span> {
        material.transparent = value &lt; <span class="hljs-number">1.0</span>;
        material.opacity = value;
    });
});

<span class="hljs-comment">// Wireframe toggle</span>
<span class="hljs-keyword">let</span> wireframeEnabled = <span class="hljs-literal">false</span>;
wireframeBtn.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function">() =&gt;</span> {
    wireframeEnabled = !wireframeEnabled;
    materials.forEach(<span class="hljs-function"><span class="hljs-params">material</span> =&gt;</span> {
        material.wireframe = wireframeEnabled;
    });
});

<span class="hljs-comment">// Emissive (glow) toggle</span>
<span class="hljs-keyword">let</span> emissiveEnabled = <span class="hljs-literal">false</span>;
emissiveBtn.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function">() =&gt;</span> {
    emissiveEnabled = !emissiveEnabled;

    <span class="hljs-comment">// Basic material doesn't have emissive property</span>
    lambertMaterial.emissive.setHex(emissiveEnabled ? <span class="hljs-number">0x002222</span> : <span class="hljs-number">0x000000</span>);
    phongMaterial.emissive.setHex(emissiveEnabled ? <span class="hljs-number">0x222200</span> : <span class="hljs-number">0x000000</span>);
    standardMaterial.emissive.setHex(emissiveEnabled ? <span class="hljs-number">0x002200</span> : <span class="hljs-number">0x000000</span>);
    physicalMaterial.emissive.setHex(emissiveEnabled ? <span class="hljs-number">0x220022</span> : <span class="hljs-number">0x000000</span>);
});
</code></pre>
<h3 id="heading-step-7-renderer-setup">Step 7: Renderer Setup</h3>
<pre><code class="lang-jsx"><span class="hljs-comment">// Create renderer</span>
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({
    <span class="hljs-attr">canvas</span>: canvas,
    <span class="hljs-attr">antialias</span>: <span class="hljs-literal">true</span>
});
renderer.setPixelRatio(<span class="hljs-built_in">window</span>.devicePixelRatio);
renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);
</code></pre>
<h3 id="heading-step-8-animation-loop">Step 8: Animation Loop</h3>
<pre><code class="lang-jsx"><span class="hljs-comment">// Animation loop - rotate spheres and animate lights</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
    requestAnimationFrame(animate);

    <span class="hljs-keyword">const</span> time = <span class="hljs-built_in">Date</span>.now() * <span class="hljs-number">0.001</span>; <span class="hljs-comment">// Convert to seconds</span>

    <span class="hljs-comment">// Rotate all spheres on Y axis</span>
    spheres.forEach(<span class="hljs-function"><span class="hljs-params">sphere</span> =&gt;</span> {
        sphere.rotation.y = time * <span class="hljs-number">0.5</span>;
        sphere.rotation.x = <span class="hljs-built_in">Math</span>.sin(time * <span class="hljs-number">0.3</span>) * <span class="hljs-number">0.2</span>;
    });

    <span class="hljs-comment">// Animate point lights in circles</span>
    pointLight.position.x = <span class="hljs-built_in">Math</span>.sin(time) * <span class="hljs-number">8</span>;
    pointLight.position.z = <span class="hljs-built_in">Math</span>.cos(time) * <span class="hljs-number">8</span>;

    pointLight2.position.x = <span class="hljs-built_in">Math</span>.sin(time + <span class="hljs-built_in">Math</span>.PI) * <span class="hljs-number">8</span>;
    pointLight2.position.z = <span class="hljs-built_in">Math</span>.cos(time + <span class="hljs-built_in">Math</span>.PI) * <span class="hljs-number">8</span>;

    renderer.render(scene, camera);
}

animate();

<span class="hljs-comment">// Handle window resize</span>
<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'resize'</span>, <span class="hljs-function">() =&gt;</span> {
    camera.aspect = <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);
});
</code></pre>
<h2 id="heading-complete-code">Complete Code</h2>
<p>Here's everything together:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"canvas"</span>);

<span class="hljs-comment">// Scene setup</span>
<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
scene.background = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-number">0x1a1a2e</span>);

<span class="hljs-comment">// Camera setup</span>
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(
    <span class="hljs-number">75</span>,
    <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight,
    <span class="hljs-number">0.1</span>,
    <span class="hljs-number">1000</span>
);
camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">15</span>);
camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

<span class="hljs-comment">// Lighting</span>
<span class="hljs-keyword">const</span> ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.3</span>);
scene.add(ambientLight);

<span class="hljs-keyword">const</span> directionalLight = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">1</span>);
directionalLight.position.set(<span class="hljs-number">5</span>, <span class="hljs-number">5</span>, <span class="hljs-number">5</span>);
scene.add(directionalLight);

<span class="hljs-keyword">const</span> pointLight = <span class="hljs-keyword">new</span> THREE.PointLight(<span class="hljs-number">0xff00ff</span>, <span class="hljs-number">1</span>);
pointLight.position.set(<span class="hljs-number">-5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>);
scene.add(pointLight);

<span class="hljs-keyword">const</span> pointLight2 = <span class="hljs-keyword">new</span> THREE.PointLight(<span class="hljs-number">0x00ffff</span>, <span class="hljs-number">0.8</span>);
pointLight2.position.set(<span class="hljs-number">5</span>, <span class="hljs-number">-3</span>, <span class="hljs-number">5</span>);
scene.add(pointLight2);

<span class="hljs-comment">// Materials</span>
<span class="hljs-keyword">const</span> basicMaterial = <span class="hljs-keyword">new</span> THREE.MeshBasicMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xff6b6b</span>,
    <span class="hljs-attr">wireframe</span>: <span class="hljs-literal">false</span>
});

<span class="hljs-keyword">const</span> lambertMaterial = <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0x4ecdc4</span>,
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0x000000</span>,
    <span class="hljs-attr">wireframe</span>: <span class="hljs-literal">false</span>
});

<span class="hljs-keyword">const</span> phongMaterial = <span class="hljs-keyword">new</span> THREE.MeshPhongMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xffe66d</span>,
    <span class="hljs-attr">shininess</span>: <span class="hljs-number">100</span>,
    <span class="hljs-attr">specular</span>: <span class="hljs-number">0xffffff</span>,
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0x000000</span>,
    <span class="hljs-attr">wireframe</span>: <span class="hljs-literal">false</span>
});

<span class="hljs-keyword">const</span> standardMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xa8e6cf</span>,
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.5</span>,
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.5</span>,
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0x000000</span>,
    <span class="hljs-attr">wireframe</span>: <span class="hljs-literal">false</span>
});

<span class="hljs-keyword">const</span> physicalMaterial = <span class="hljs-keyword">new</span> THREE.MeshPhysicalMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xffa8e8</span>,
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.8</span>,
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.2</span>,
    <span class="hljs-attr">clearcoat</span>: <span class="hljs-number">1.0</span>,
    <span class="hljs-attr">clearcoatRoughness</span>: <span class="hljs-number">0.1</span>,
    <span class="hljs-attr">reflectivity</span>: <span class="hljs-number">1.0</span>,
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0x000000</span>,
    <span class="hljs-attr">wireframe</span>: <span class="hljs-literal">false</span>
});

<span class="hljs-comment">// Create spheres</span>
<span class="hljs-keyword">const</span> sphereGeometry = <span class="hljs-keyword">new</span> THREE.SphereGeometry(<span class="hljs-number">1.5</span>, <span class="hljs-number">64</span>, <span class="hljs-number">64</span>);

<span class="hljs-keyword">const</span> basicSphere = <span class="hljs-keyword">new</span> THREE.Mesh(sphereGeometry, basicMaterial);
basicSphere.position.set(<span class="hljs-number">-8</span>, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>);
scene.add(basicSphere);

<span class="hljs-keyword">const</span> lambertSphere = <span class="hljs-keyword">new</span> THREE.Mesh(sphereGeometry, lambertMaterial);
lambertSphere.position.set(<span class="hljs-number">-4</span>, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>);
scene.add(lambertSphere);

<span class="hljs-keyword">const</span> phongSphere = <span class="hljs-keyword">new</span> THREE.Mesh(sphereGeometry, phongMaterial);
phongSphere.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>);
scene.add(phongSphere);

<span class="hljs-keyword">const</span> standardSphere = <span class="hljs-keyword">new</span> THREE.Mesh(sphereGeometry, standardMaterial);
standardSphere.position.set(<span class="hljs-number">4</span>, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>);
scene.add(standardSphere);

<span class="hljs-keyword">const</span> physicalSphere = <span class="hljs-keyword">new</span> THREE.Mesh(sphereGeometry, physicalMaterial);
physicalSphere.position.set(<span class="hljs-number">8</span>, <span class="hljs-number">2</span>, <span class="hljs-number">0</span>);
scene.add(physicalSphere);

<span class="hljs-keyword">const</span> spheres = [basicSphere, lambertSphere, phongSphere, standardSphere, physicalSphere];
<span class="hljs-keyword">const</span> materials = [basicMaterial, lambertMaterial, phongMaterial, standardMaterial, physicalMaterial];

<span class="hljs-comment">// Create labels</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createTextLabel</span>(<span class="hljs-params">text</span>) </span>{
    <span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'canvas'</span>);
    <span class="hljs-keyword">const</span> context = canvas.getContext(<span class="hljs-string">'2d'</span>);
    canvas.width = <span class="hljs-number">512</span>;
    canvas.height = <span class="hljs-number">128</span>;

    context.fillStyle = <span class="hljs-string">'#000000'</span>;
    context.fillRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, canvas.width, canvas.height);

    context.font = <span class="hljs-string">'Bold 48px Arial'</span>;
    context.fillStyle = <span class="hljs-string">'#ffffff'</span>;
    context.textAlign = <span class="hljs-string">'center'</span>;
    context.fillText(text, canvas.width / <span class="hljs-number">2</span>, canvas.height / <span class="hljs-number">2</span> + <span class="hljs-number">16</span>);

    <span class="hljs-keyword">const</span> texture = <span class="hljs-keyword">new</span> THREE.CanvasTexture(canvas);
    <span class="hljs-keyword">const</span> spriteMaterial = <span class="hljs-keyword">new</span> THREE.SpriteMaterial({ <span class="hljs-attr">map</span>: texture });
    <span class="hljs-keyword">const</span> sprite = <span class="hljs-keyword">new</span> THREE.Sprite(spriteMaterial);
    sprite.scale.set(<span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>);

    <span class="hljs-keyword">return</span> sprite;
}

<span class="hljs-keyword">const</span> labels = [
    { <span class="hljs-attr">text</span>: <span class="hljs-string">'Basic'</span>, <span class="hljs-attr">position</span>: [<span class="hljs-number">-8</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] },
    { <span class="hljs-attr">text</span>: <span class="hljs-string">'Lambert'</span>, <span class="hljs-attr">position</span>: [<span class="hljs-number">-4</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] },
    { <span class="hljs-attr">text</span>: <span class="hljs-string">'Phong'</span>, <span class="hljs-attr">position</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] },
    { <span class="hljs-attr">text</span>: <span class="hljs-string">'Standard'</span>, <span class="hljs-attr">position</span>: [<span class="hljs-number">4</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] },
    { <span class="hljs-attr">text</span>: <span class="hljs-string">'Physical'</span>, <span class="hljs-attr">position</span>: [<span class="hljs-number">8</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] }
];

labels.forEach(<span class="hljs-function"><span class="hljs-params">label</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> sprite = createTextLabel(label.text);
    sprite.position.set(label.position[<span class="hljs-number">0</span>], label.position[<span class="hljs-number">1</span>], label.position[<span class="hljs-number">2</span>]);
    scene.add(sprite);
});

<span class="hljs-comment">// Interactive controls</span>
<span class="hljs-keyword">const</span> metalnessSlider = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'metalness'</span>);
<span class="hljs-keyword">const</span> roughnessSlider = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'roughness'</span>);
<span class="hljs-keyword">const</span> opacitySlider = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'opacity'</span>);
<span class="hljs-keyword">const</span> wireframeBtn = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'toggleWireframe'</span>);
<span class="hljs-keyword">const</span> emissiveBtn = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'toggleEmissive'</span>);

<span class="hljs-keyword">const</span> metalnessValue = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'metalnessValue'</span>);
<span class="hljs-keyword">const</span> roughnessValue = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'roughnessValue'</span>);
<span class="hljs-keyword">const</span> opacityValue = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'opacityValue'</span>);

metalnessSlider.addEventListener(<span class="hljs-string">'input'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">parseFloat</span>(e.target.value);
    metalnessValue.textContent = value.toFixed(<span class="hljs-number">1</span>);
    standardMaterial.metalness = value;
    physicalMaterial.metalness = value;
});

roughnessSlider.addEventListener(<span class="hljs-string">'input'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">parseFloat</span>(e.target.value);
    roughnessValue.textContent = value.toFixed(<span class="hljs-number">1</span>);
    standardMaterial.roughness = value;
    physicalMaterial.roughness = value;
});

opacitySlider.addEventListener(<span class="hljs-string">'input'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">parseFloat</span>(e.target.value);
    opacityValue.textContent = value.toFixed(<span class="hljs-number">1</span>);
    materials.forEach(<span class="hljs-function"><span class="hljs-params">material</span> =&gt;</span> {
        material.transparent = value &lt; <span class="hljs-number">1.0</span>;
        material.opacity = value;
    });
});

<span class="hljs-keyword">let</span> wireframeEnabled = <span class="hljs-literal">false</span>;
wireframeBtn.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function">() =&gt;</span> {
    wireframeEnabled = !wireframeEnabled;
    materials.forEach(<span class="hljs-function"><span class="hljs-params">material</span> =&gt;</span> {
        material.wireframe = wireframeEnabled;
    });
});

<span class="hljs-keyword">let</span> emissiveEnabled = <span class="hljs-literal">false</span>;
emissiveBtn.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function">() =&gt;</span> {
    emissiveEnabled = !emissiveEnabled;
    lambertMaterial.emissive.setHex(emissiveEnabled ? <span class="hljs-number">0x002222</span> : <span class="hljs-number">0x000000</span>);
    phongMaterial.emissive.setHex(emissiveEnabled ? <span class="hljs-number">0x222200</span> : <span class="hljs-number">0x000000</span>);
    standardMaterial.emissive.setHex(emissiveEnabled ? <span class="hljs-number">0x002200</span> : <span class="hljs-number">0x000000</span>);
    physicalMaterial.emissive.setHex(emissiveEnabled ? <span class="hljs-number">0x220022</span> : <span class="hljs-number">0x000000</span>);
});

<span class="hljs-comment">// Renderer</span>
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({
    <span class="hljs-attr">canvas</span>: canvas,
    <span class="hljs-attr">antialias</span>: <span class="hljs-literal">true</span>
});
renderer.setPixelRatio(<span class="hljs-built_in">window</span>.devicePixelRatio);
renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);

<span class="hljs-comment">// Animation loop</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
    requestAnimationFrame(animate);

    <span class="hljs-keyword">const</span> time = <span class="hljs-built_in">Date</span>.now() * <span class="hljs-number">0.001</span>;

    spheres.forEach(<span class="hljs-function"><span class="hljs-params">sphere</span> =&gt;</span> {
        sphere.rotation.y = time * <span class="hljs-number">0.5</span>;
        sphere.rotation.x = <span class="hljs-built_in">Math</span>.sin(time * <span class="hljs-number">0.3</span>) * <span class="hljs-number">0.2</span>;
    });

    pointLight.position.x = <span class="hljs-built_in">Math</span>.sin(time) * <span class="hljs-number">8</span>;
    pointLight.position.z = <span class="hljs-built_in">Math</span>.cos(time) * <span class="hljs-number">8</span>;

    pointLight2.position.x = <span class="hljs-built_in">Math</span>.sin(time + <span class="hljs-built_in">Math</span>.PI) * <span class="hljs-number">8</span>;
    pointLight2.position.z = <span class="hljs-built_in">Math</span>.cos(time + <span class="hljs-built_in">Math</span>.PI) * <span class="hljs-number">8</span>;

    renderer.render(scene, camera);
}

animate();

<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'resize'</span>, <span class="hljs-function">() =&gt;</span> {
    camera.aspect = <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);
});
</code></pre>
<h2 id="heading-when-to-use-each-material">When to Use Each Material</h2>
<h3 id="heading-meshbasicmaterial">MeshBasicMaterial</h3>
<p><strong>Performance:</strong> ⚡⚡⚡⚡⚡ (Fastest)</p>
<p><strong>When to use:</strong></p>
<ul>
<li><p>UI elements that should always be visible</p>
</li>
<li><p>Backgrounds or sky boxes</p>
</li>
<li><p>Debug visualizations</p>
</li>
<li><p>Objects that shouldn't react to lights</p>
</li>
<li><p>Performance-critical applications with many objects</p>
</li>
</ul>
<p><strong>Pros:</strong> No lighting calculations, always renders fast</p>
<p><strong>Cons:</strong> Looks flat, no depth or realism</p>
<h3 id="heading-meshlambertmaterial">MeshLambertMaterial</h3>
<p><strong>Performance:</strong> ⚡⚡⚡⚡ (Fast)</p>
<p><strong>When to use:</strong></p>
<ul>
<li><p>Matte, non-shiny surfaces</p>
</li>
<li><p>Paper, cardboard, unfinished wood</p>
</li>
<li><p>Fabric, cloth materials</p>
</li>
<li><p>Chalk, matte paint</p>
</li>
<li><p>Mobile applications (good performance)</p>
</li>
</ul>
<p><strong>Pros:</strong> Good performance, realistic for matte surfaces</p>
<p><strong>Cons:</strong> No specular highlights (can't make shiny things)</p>
<h3 id="heading-meshphongmaterial">MeshPhongMaterial</h3>
<p><strong>Performance:</strong> ⚡⚡⚡ (Medium)</p>
<p><strong>When to use:</strong></p>
<ul>
<li><p>Shiny plastics</p>
</li>
<li><p>Glossy paint</p>
</li>
<li><p>Polished wood</p>
</li>
<li><p>Ceramic surfaces</p>
</li>
<li><p>When you need visible light reflections</p>
</li>
</ul>
<p><strong>Pros:</strong> Good specular highlights, proven technique</p>
<p><strong>Cons:</strong> Not physically accurate, being replaced by Standard material</p>
<h3 id="heading-meshstandardmaterial">MeshStandardMaterial</h3>
<p><strong>Performance:</strong> ⚡⚡ (Slower)</p>
<p><strong>When to use:</strong></p>
<ul>
<li><p>Modern, realistic rendering</p>
</li>
<li><p>Metals (using metalness)</p>
</li>
<li><p>Most general-purpose 3D objects</p>
</li>
<li><p>When you want physically accurate materials</p>
</li>
<li><p>Production-quality projects</p>
</li>
</ul>
<p><strong>Pros:</strong> Physically accurate (PBR), versatile, industry standard</p>
<p><strong>Cons:</strong> More expensive than Basic/Lambert/Phong</p>
<h3 id="heading-meshphysicalmaterial">MeshPhysicalMaterial</h3>
<p><strong>Performance:</strong> ⚡ (Slowest)</p>
<p><strong>When to use:</strong></p>
<ul>
<li><p>Car paint with clear coat</p>
</li>
<li><p>Gems and jewellery</p>
</li>
<li><p>Glass and transparent materials</p>
</li>
<li><p>High-end product visualization</p>
</li>
<li><p>When you need the absolute best quality</p>
</li>
</ul>
<p><strong>Pros:</strong> Most realistic, has advanced features (clear coat, transmission)</p>
<p><strong>Cons:</strong> Most expensive performance-wise, overkill for many use cases</p>
<h2 id="heading-performance-comparison">Performance Comparison</h2>
<p>From fastest to slowest:</p>
<ol>
<li><p><strong>MeshBasicMaterial</strong> - No lighting calculations</p>
</li>
<li><p><strong>MeshLambertMaterial</strong> - Simple lighting (diffuse only)</p>
</li>
<li><p><strong>MeshPhongMaterial</strong> - Lighting + specular highlights</p>
</li>
<li><p><strong>MeshStandardMaterial</strong> - Full PBR calculations</p>
</li>
<li><p><strong>MeshPhysicalMaterial</strong> - PBR + advanced effects</p>
</li>
</ol>
<p><strong>Pro tip:</strong> Use the cheapest material that gives you the look you need! Don't use Physical material if Standard looks good enough.</p>
<h2 id="heading-what-i-learned">What I Learned</h2>
<h3 id="heading-1-materials-define-appearance">1. <strong>Materials Define Appearance</strong></h3>
<p>While geometry defines shape, materials define how that shape looks - the colour, shininess, transparency, and how it reacts to light. They're equally important!</p>
<h3 id="heading-2-lighting-dependencies-matter">2. <strong>Lighting Dependencies Matter</strong></h3>
<ul>
<li><p><strong>Basic material</strong> = No lights needed (always visible)</p>
</li>
<li><p><strong>Lambert, Phong, Standard, Physical</strong> = Need lights to be visible This is crucial when debugging - if your objects are black, check your lights!</p>
</li>
</ul>
<h3 id="heading-3-pbr-is-the-modern-standard">3. <strong>PBR is the Modern Standard</strong></h3>
<p>The metalness/roughness workflow (Standard and Physical materials) is what modern games and applications use. It's more intuitive than the old specular workflow.</p>
<h3 id="heading-4-metalness-vs-roughness">4. <strong>Metalness vs Roughness</strong></h3>
<p>Understanding these two properties unlocks realistic materials:</p>
<ul>
<li><p><strong>Metalness:</strong> Is it metal or not? (Usually 0 or 1, rarely in between)</p>
</li>
<li><p><strong>Roughness:</strong> How polished is the surface? (0 = mirror, 1 = matte)</p>
</li>
</ul>
<h3 id="heading-5-performance-vs-quality-trade-off">5. <strong>Performance vs Quality Trade-off</strong></h3>
<p>You can have 1000 objects with Basic material or 100 with Physical material. Choose based on your needs:</p>
<ul>
<li><p>Mobile/many objects = Basic or Lambert</p>
</li>
<li><p>Desktop/quality matters = Standard or Physical</p>
</li>
</ul>
<h3 id="heading-6-emissive-adds-depth">6. <strong>Emissive Adds Depth</strong></h3>
<p>Adding a subtle emissive glow (same colour as the object, very low intensity) can make materials look more alive without affecting performance much.</p>
<h3 id="heading-7-transparency-requires-planning">7. <strong>Transparency Requires Planning</strong></h3>
<p>When using opacity, you must set <code>transparent: true</code> first. Also, transparent objects can have sorting issues when overlapping - Three.js renders them back-to-front.</p>
<h3 id="heading-8-wireframe-mode-is-essential-for-learning">8. <strong>Wireframe Mode is Essential for Learning</strong></h3>
<p>Toggle wireframe mode to see the actual geometry structure. It's incredibly useful for understanding how shapes are built and debugging performance issues.</p>
<h2 id="heading-common-material-patterns">Common Material Patterns</h2>
<p>Here are some material configurations I've found useful:</p>
<h3 id="heading-polished-metal">Polished Metal</h3>
<pre><code class="lang-jsx"><span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xaaaaaa</span>,
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">1.0</span>,
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.2</span>
});
</code></pre>
<h3 id="heading-matte-plastic">Matte Plastic</h3>
<pre><code class="lang-jsx"><span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0x2194ce</span>,
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.0</span>,
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.8</span>
});
</code></pre>
<h3 id="heading-glass">Glass</h3>
<pre><code class="lang-jsx"><span class="hljs-keyword">new</span> THREE.MeshPhysicalMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xffffff</span>,
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.0</span>,
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.0</span>,
    <span class="hljs-attr">transmission</span>: <span class="hljs-number">1.0</span>,
    <span class="hljs-attr">transparent</span>: <span class="hljs-literal">true</span>
});
</code></pre>
<h3 id="heading-glowing-object">Glowing Object</h3>
<pre><code class="lang-jsx"><span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0x00ff00</span>,
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0x00ff00</span>,
    <span class="hljs-attr">emissiveIntensity</span>: <span class="hljs-number">0.5</span>
});
</code></pre>
<h2 id="heading-key-takeaways"><strong>Key takeaways:</strong></h2>
<ul>
<li><p>Basic = fast but flat</p>
</li>
<li><p>Lambert = matte surfaces</p>
</li>
<li><p>Phong = shiny surfaces</p>
</li>
<li><p>Standard = modern PBR (use this most)</p>
</li>
<li><p>Physical = premium quality when you need it</p>
</li>
</ul>
<p>The interactive controls in this demo really helped me understand how metalness, roughness, and other properties affect the final look. I recommend building this yourself and playing with the sliders!</p>
<p>Try experimenting with:</p>
<ul>
<li><p>Different metalness/roughness combinations</p>
</li>
<li><p>Adding emissive glows in different colors</p>
</li>
<li><p>Creating transparent materials</p>
</li>
<li><p>Mixing materials on different objects</p>
</li>
<li><p>Adjusting lighting to see how materials react</p>
</li>
</ul>
<p>Understanding materials is essential for creating visually appealing 3D experiences. Now I can make things look like metal, plastic, glass, or anything in between!</p>
<p>Happy rendering. 🎨</p>
]]></content:encoded></item><item><title><![CDATA[Exploring Three.js Geometries: The Building Blocks of 3D]]></title><description><![CDATA[Welcome back to my Three.js learning journey! 👋 In my previous post, I covered the basics of setting up a Three.js scene. Today, I'm diving deeper into geometries - the fundamental shapes that form every 3D object you see on the web.
Think of geomet...]]></description><link>https://blog.iamdipankarpaul.com/exploring-threejs-geometries-the-building-blocks-of-3d</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/exploring-threejs-geometries-the-building-blocks-of-3d</guid><category><![CDATA[ThreeJS]]></category><category><![CDATA[React]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Sat, 03 Jan 2026 15:25:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767452684332/50bbd89b-bbef-4753-a539-b176ae43b383.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome back to my Three.js learning journey! 👋 In my <a target="_blank" href="https://blog.iamdipankarpaul.com/my-first-threejs-scene-building-a-3d-world-in-the-browser">previous post</a>, I covered the basics of setting up a Three.js scene. Today, I'm diving deeper into <strong>geometries</strong> - the fundamental shapes that form every 3D object you see on the web.</p>
<p>Think of geometries like LEGO pieces. Each piece has a different shape, but they all work the same way - you attach them to materials, position them in your scene and you've got 3D objects.</p>
<h2 id="heading-what-are-geometries">What Are Geometries?</h2>
<p>In Three.js, a geometry defines the <strong>shape</strong> of a 3D object. It's essentially a collection of:</p>
<ul>
<li><p><strong>Vertices</strong> (points in 3D space)</p>
</li>
<li><p><strong>Faces</strong> (triangles connecting those points)</p>
</li>
</ul>
<p>Three.js provides many built-in geometries that cover most use cases. Today, we'll explore four essential shapes and understand when to use each one.</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>Before we jump into different geometries, here's our basic HTML structure:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Three.js Geometries Gallery<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
        <span class="hljs-selector-tag">body</span> {
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
            <span class="hljs-attribute">overflow</span>: hidden;
            <span class="hljs-attribute">font-family</span>: Arial, sans-serif;
        }
        <span class="hljs-selector-tag">canvas</span> {
            <span class="hljs-attribute">display</span>: block;
        }
        <span class="hljs-selector-id">#info</span> {
            <span class="hljs-attribute">position</span>: absolute;
            <span class="hljs-attribute">top</span>: <span class="hljs-number">20px</span>;
            <span class="hljs-attribute">left</span>: <span class="hljs-number">20px</span>;
            <span class="hljs-attribute">color</span>: white;
            <span class="hljs-attribute">background</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.7</span>);
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">15px</span>;
            <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
            <span class="hljs-attribute">font-size</span>: <span class="hljs-number">14px</span>;
        }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"canvas"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"info"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Geometries Gallery<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"script.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h2 id="heading-building-our-geometry-gallery">Building Our Geometry Gallery</h2>
<p>Now let's create a gallery showcasing different Three.js geometries. I'll arrange them in a nice grid pattern so we can see them all at once!</p>
<h3 id="heading-step-1-scene-setup">Step 1: Scene Setup</h3>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"canvas"</span>);

<span class="hljs-comment">// Scene setup</span>
<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
scene.background = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-number">0x1a1a1a</span>); <span class="hljs-comment">// Dark background</span>

<span class="hljs-comment">// Camera setup</span>
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(
    <span class="hljs-number">75</span>, <span class="hljs-comment">// Field of view</span>
    <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight, <span class="hljs-comment">// Aspect ratio</span>
    <span class="hljs-number">0.1</span>, <span class="hljs-comment">// Near clipping plane</span>
    <span class="hljs-number">1000</span> <span class="hljs-comment">// Far clipping plane</span>
);
camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">3</span>, <span class="hljs-number">12</span>); <span class="hljs-comment">// Position camera to see all objects</span>
camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>); <span class="hljs-comment">// Look at center of scene</span>
</code></pre>
<h3 id="heading-step-2-lighting-setup">Step 2: Lighting Setup</h3>
<pre><code class="lang-jsx"><span class="hljs-comment">// Ambient light - provides base illumination for all objects</span>
<span class="hljs-keyword">const</span> ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.4</span>);
scene.add(ambientLight);

<span class="hljs-comment">// Directional light - acts like sunlight from a specific direction</span>
<span class="hljs-keyword">const</span> directionalLight = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.8</span>);
directionalLight.position.set(<span class="hljs-number">5</span>, <span class="hljs-number">10</span>, <span class="hljs-number">5</span>);
scene.add(directionalLight);

<span class="hljs-comment">// Point light - adds a colored accent light</span>
<span class="hljs-keyword">const</span> pointLight = <span class="hljs-keyword">new</span> THREE.PointLight(<span class="hljs-number">0x00ffff</span>, <span class="hljs-number">0.5</span>);
pointLight.position.set(<span class="hljs-number">-5</span>, <span class="hljs-number">5</span>, <span class="hljs-number">5</span>);
scene.add(pointLight);
</code></pre>
<h3 id="heading-step-3-creating-different-geometries">Step 3: Creating Different Geometries</h3>
<p>Now for the fun part - let's create four essential geometry types!</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// ============================================</span>
<span class="hljs-comment">// SPHERE GEOMETRY - Perfect for balls, planets, bubbles</span>
<span class="hljs-comment">// ============================================</span>
<span class="hljs-keyword">const</span> sphereGeometry = <span class="hljs-keyword">new</span> THREE.SphereGeometry(
    <span class="hljs-number">1</span>,      <span class="hljs-comment">// radius</span>
    <span class="hljs-number">32</span>,     <span class="hljs-comment">// widthSegments (more = smoother)</span>
    <span class="hljs-number">32</span>      <span class="hljs-comment">// heightSegments (more = smoother)</span>
);
<span class="hljs-keyword">const</span> sphereMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xff6b6b</span>,
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.3</span>,
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.4</span>
});
<span class="hljs-keyword">const</span> sphere = <span class="hljs-keyword">new</span> THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.set(<span class="hljs-number">-4.5</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>); <span class="hljs-comment">// Position on the left</span>
scene.add(sphere);

<span class="hljs-comment">// ============================================</span>
<span class="hljs-comment">// CYLINDER GEOMETRY - Great for pillars, cans, tubes</span>
<span class="hljs-comment">// ============================================</span>
<span class="hljs-keyword">const</span> cylinderGeometry = <span class="hljs-keyword">new</span> THREE.CylinderGeometry(
    <span class="hljs-number">1</span>,      <span class="hljs-comment">// radiusTop</span>
    <span class="hljs-number">1</span>,      <span class="hljs-comment">// radiusBottom</span>
    <span class="hljs-number">2</span>,      <span class="hljs-comment">// height</span>
    <span class="hljs-number">32</span>      <span class="hljs-comment">// radialSegments (more = smoother circular edge)</span>
);
<span class="hljs-keyword">const</span> cylinderMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0x4ecdc4</span>,
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.5</span>,
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.3</span>
});
<span class="hljs-keyword">const</span> cylinder = <span class="hljs-keyword">new</span> THREE.Mesh(cylinderGeometry, cylinderMaterial);
cylinder.position.set(<span class="hljs-number">-1.5</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>); <span class="hljs-comment">// Position center-left</span>
scene.add(cylinder);

<span class="hljs-comment">// ============================================</span>
<span class="hljs-comment">// CONE GEOMETRY - Perfect for traffic cones, mountains, party hats</span>
<span class="hljs-comment">// ============================================</span>
<span class="hljs-keyword">const</span> coneGeometry = <span class="hljs-keyword">new</span> THREE.ConeGeometry(
    <span class="hljs-number">1</span>,      <span class="hljs-comment">// radius of base</span>
    <span class="hljs-number">2</span>,      <span class="hljs-comment">// height</span>
    <span class="hljs-number">32</span>      <span class="hljs-comment">// radialSegments</span>
);
<span class="hljs-keyword">const</span> coneMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xffe66d</span>,
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.2</span>,
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.6</span>
});
<span class="hljs-keyword">const</span> cone = <span class="hljs-keyword">new</span> THREE.Mesh(coneGeometry, coneMaterial);
cone.position.set(<span class="hljs-number">1.5</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>); <span class="hljs-comment">// Position center-right</span>
scene.add(cone);

<span class="hljs-comment">// ============================================</span>
<span class="hljs-comment">// TORUS GEOMETRY - Donut shape, great for rings, hoops</span>
<span class="hljs-comment">// ============================================</span>
<span class="hljs-keyword">const</span> torusGeometry = <span class="hljs-keyword">new</span> THREE.TorusGeometry(
    <span class="hljs-number">1</span>,      <span class="hljs-comment">// radius of torus (donut size)</span>
    <span class="hljs-number">0.4</span>,    <span class="hljs-comment">// tube radius (thickness of donut)</span>
    <span class="hljs-number">16</span>,     <span class="hljs-comment">// radialSegments</span>
    <span class="hljs-number">100</span>     <span class="hljs-comment">// tubularSegments (smoothness around the tube)</span>
);
<span class="hljs-keyword">const</span> torusMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xa8e6cf</span>,
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.6</span>,
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.2</span>
});
<span class="hljs-keyword">const</span> torus = <span class="hljs-keyword">new</span> THREE.Mesh(torusGeometry, torusMaterial);
torus.position.set(<span class="hljs-number">4.5</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>); <span class="hljs-comment">// Position on the right</span>
scene.add(torus);
</code></pre>
<h2 id="heading-understanding-geometry-parameters">Understanding Geometry Parameters</h2>
<p>Let me break down what those numbers in each geometry mean and when you'd adjust them:</p>
<h3 id="heading-spheregeometryradius-widthsegments-heightsegments">SphereGeometry(radius, widthSegments, heightSegments)</h3>
<ul>
<li><p><strong>radius</strong>: Size of the sphere</p>
</li>
<li><p><strong>widthSegments/heightSegments</strong>: Higher numbers = smoother sphere, but more performance cost</p>
</li>
<li><p><strong>Use for</strong>: Planets, balls, bubbles, orbs</p>
</li>
</ul>
<h3 id="heading-cylindergeometryradiustop-radiusbottom-height-radialsegments">CylinderGeometry(radiusTop, radiusBottom, height, radialSegments)</h3>
<ul>
<li><p><strong>radiusTop/radiusBottom</strong>: Make them different to create tapered shapes!</p>
</li>
<li><p><strong>height</strong>: How tall the cylinder is</p>
</li>
<li><p><strong>radialSegments</strong>: Higher = smoother circular edges</p>
</li>
<li><p><strong>Use for</strong>: Pillars, cans, tubes, tree trunks</p>
</li>
</ul>
<h3 id="heading-conegeometryradius-height-radialsegments">ConeGeometry(radius, height, radialSegments)</h3>
<ul>
<li><p><strong>radius</strong>: Base size</p>
</li>
<li><p><strong>height</strong>: How tall the cone is</p>
</li>
<li><p><strong>radialSegments</strong>: Smoothness of the base circle</p>
</li>
<li><p><strong>Use for</strong>: Party hats, mountains, traffic cones, arrowheads</p>
</li>
</ul>
<h3 id="heading-torusgeometryradius-tube-radialsegments-tubularsegments">TorusGeometry(radius, tube, radialSegments, tubularSegments)</h3>
<ul>
<li><p><strong>radius</strong>: Overall donut size</p>
</li>
<li><p><strong>tube</strong>: Thickness of the donut ring</p>
</li>
<li><p><strong>radialSegments/tubularSegments</strong>: Smoothness</p>
</li>
<li><p><strong>Use for</strong>: Rings, hoops, tire shapes</p>
</li>
</ul>
<h3 id="heading-step-4-adding-a-reference-grid">Step 4: Adding a Reference Grid</h3>
<p>Let's add a grid to help visualize the 3D space:</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// Grid helper - shows the ground plane</span>
<span class="hljs-keyword">const</span> gridHelper = <span class="hljs-keyword">new</span> THREE.GridHelper(<span class="hljs-number">20</span>, <span class="hljs-number">20</span>, <span class="hljs-number">0x444444</span>, <span class="hljs-number">0x222222</span>);
gridHelper.position.y = <span class="hljs-number">-3</span>;
scene.add(gridHelper);
</code></pre>
<h3 id="heading-step-5-renderer-setup">Step 5: Renderer Setup</h3>
<pre><code class="lang-jsx"><span class="hljs-comment">// Create renderer</span>
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({
    <span class="hljs-attr">canvas</span>: canvas,
    <span class="hljs-attr">antialias</span>: <span class="hljs-literal">true</span> <span class="hljs-comment">// Smooth edges</span>
});
renderer.setPixelRatio(<span class="hljs-built_in">window</span>.devicePixelRatio);
renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);
</code></pre>
<h3 id="heading-step-6-animation-loop">Step 6: Animation Loop</h3>
<pre><code class="lang-jsx"><span class="hljs-comment">// Animation loop - rotate all objects for better viewing</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
    requestAnimationFrame(animate);

    <span class="hljs-comment">// Rotate all geometries on multiple axes</span>
    <span class="hljs-keyword">const</span> time = <span class="hljs-built_in">Date</span>.now() * <span class="hljs-number">0.001</span>; <span class="hljs-comment">// Convert to seconds</span>

    sphere.rotation.x = time * <span class="hljs-number">0.5</span>;
    sphere.rotation.y = time * <span class="hljs-number">0.7</span>;

    cylinder.rotation.x = time * <span class="hljs-number">0.3</span>;
    cylinder.rotation.y = time * <span class="hljs-number">0.5</span>;

    cone.rotation.x = time * <span class="hljs-number">0.4</span>;
    cone.rotation.y = time * <span class="hljs-number">0.6</span>;

    torus.rotation.x = time * <span class="hljs-number">0.6</span>;
    torus.rotation.y = time * <span class="hljs-number">0.4</span>;

    renderer.render(scene, camera);
}

animate();

<span class="hljs-comment">// Handle window resize</span>
<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'resize'</span>, <span class="hljs-function">() =&gt;</span> {
    camera.aspect = <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);
});
</code></pre>
<h2 id="heading-complete-code">Complete Code</h2>
<p>Here's the full code altogether:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"canvas"</span>);

<span class="hljs-comment">// Scene setup</span>
<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
scene.background = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-number">0x1a1a1a</span>);

<span class="hljs-comment">// Camera setup</span>
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(
    <span class="hljs-number">75</span>,
    <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight,
    <span class="hljs-number">0.1</span>,
    <span class="hljs-number">1000</span>
);
camera.position.set(<span class="hljs-number">0</span>, <span class="hljs-number">3</span>, <span class="hljs-number">12</span>);
camera.lookAt(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

<span class="hljs-comment">// Lighting</span>
<span class="hljs-keyword">const</span> ambientLight = <span class="hljs-keyword">new</span> THREE.AmbientLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.4</span>);
scene.add(ambientLight);

<span class="hljs-keyword">const</span> directionalLight = <span class="hljs-keyword">new</span> THREE.DirectionalLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">0.8</span>);
directionalLight.position.set(<span class="hljs-number">5</span>, <span class="hljs-number">10</span>, <span class="hljs-number">5</span>);
scene.add(directionalLight);

<span class="hljs-keyword">const</span> pointLight = <span class="hljs-keyword">new</span> THREE.PointLight(<span class="hljs-number">0x00ffff</span>, <span class="hljs-number">0.5</span>);
pointLight.position.set(<span class="hljs-number">-5</span>, <span class="hljs-number">5</span>, <span class="hljs-number">5</span>);
scene.add(pointLight);

<span class="hljs-comment">// SPHERE</span>
<span class="hljs-keyword">const</span> sphereGeometry = <span class="hljs-keyword">new</span> THREE.SphereGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">32</span>, <span class="hljs-number">32</span>);
<span class="hljs-keyword">const</span> sphereMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xff6b6b</span>,
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.3</span>,
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.4</span>
});
<span class="hljs-keyword">const</span> sphere = <span class="hljs-keyword">new</span> THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.set(<span class="hljs-number">-4.5</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
scene.add(sphere);

<span class="hljs-comment">// CYLINDER</span>
<span class="hljs-keyword">const</span> cylinderGeometry = <span class="hljs-keyword">new</span> THREE.CylinderGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">32</span>);
<span class="hljs-keyword">const</span> cylinderMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0x4ecdc4</span>,
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.5</span>,
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.3</span>
});
<span class="hljs-keyword">const</span> cylinder = <span class="hljs-keyword">new</span> THREE.Mesh(cylinderGeometry, cylinderMaterial);
cylinder.position.set(<span class="hljs-number">-1.5</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
scene.add(cylinder);

<span class="hljs-comment">// CONE</span>
<span class="hljs-keyword">const</span> coneGeometry = <span class="hljs-keyword">new</span> THREE.ConeGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">32</span>);
<span class="hljs-keyword">const</span> coneMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xffe66d</span>,
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.2</span>,
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.6</span>
});
<span class="hljs-keyword">const</span> cone = <span class="hljs-keyword">new</span> THREE.Mesh(coneGeometry, coneMaterial);
cone.position.set(<span class="hljs-number">1.5</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
scene.add(cone);

<span class="hljs-comment">// TORUS</span>
<span class="hljs-keyword">const</span> torusGeometry = <span class="hljs-keyword">new</span> THREE.TorusGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">0.4</span>, <span class="hljs-number">16</span>, <span class="hljs-number">100</span>);
<span class="hljs-keyword">const</span> torusMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xa8e6cf</span>,
    <span class="hljs-attr">metalness</span>: <span class="hljs-number">0.6</span>,
    <span class="hljs-attr">roughness</span>: <span class="hljs-number">0.2</span>
});
<span class="hljs-keyword">const</span> torus = <span class="hljs-keyword">new</span> THREE.Mesh(torusGeometry, torusMaterial);
torus.position.set(<span class="hljs-number">4.5</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
scene.add(torus);

<span class="hljs-comment">// Grid helper</span>
<span class="hljs-keyword">const</span> gridHelper = <span class="hljs-keyword">new</span> THREE.GridHelper(<span class="hljs-number">20</span>, <span class="hljs-number">20</span>, <span class="hljs-number">0x444444</span>, <span class="hljs-number">0x222222</span>);
gridHelper.position.y = <span class="hljs-number">-3</span>;
scene.add(gridHelper);

<span class="hljs-comment">// Renderer</span>
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({
    <span class="hljs-attr">canvas</span>: canvas,
    <span class="hljs-attr">antialias</span>: <span class="hljs-literal">true</span>
});
renderer.setPixelRatio(<span class="hljs-built_in">window</span>.devicePixelRatio);
renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);

<span class="hljs-comment">// Animation loop</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animate</span>(<span class="hljs-params"></span>) </span>{
    requestAnimationFrame(animate);

    <span class="hljs-keyword">const</span> time = <span class="hljs-built_in">Date</span>.now() * <span class="hljs-number">0.001</span>;

    sphere.rotation.x = time * <span class="hljs-number">0.5</span>;
    sphere.rotation.y = time * <span class="hljs-number">0.7</span>;

    cylinder.rotation.x = time * <span class="hljs-number">0.3</span>;
    cylinder.rotation.y = time * <span class="hljs-number">0.5</span>;

    cone.rotation.x = time * <span class="hljs-number">0.4</span>;
    cone.rotation.y = time * <span class="hljs-number">0.6</span>;

    torus.rotation.x = time * <span class="hljs-number">0.6</span>;
    torus.rotation.y = time * <span class="hljs-number">0.4</span>;

    renderer.render(scene, camera);
}

animate();

<span class="hljs-comment">// Resize handler</span>
<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'resize'</span>, <span class="hljs-function">() =&gt;</span> {
    camera.aspect = <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-built_in">window</span>.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(<span class="hljs-built_in">window</span>.innerWidth, <span class="hljs-built_in">window</span>.innerHeight);
});
</code></pre>
<h2 id="heading-when-to-use-custom-geometries">When to Use Custom Geometries</h2>
<p>You'd create custom geometries when:</p>
<ol>
<li><p><strong>You need a specific shape</strong> not available in Three.js built-ins</p>
</li>
<li><p><strong>You're loading geometry data</strong> from external sources</p>
</li>
<li><p><strong>You want precise control</strong> over every vertex and face</p>
</li>
<li><p><strong>Performance matters</strong> and you need optimized shapes</p>
</li>
</ol>
<p>Custom geometries are more work, but they give you complete control!</p>
<h2 id="heading-the-core-concept-its-all-meshes">The Core Concept: It's All Meshes</h2>
<p>Here's the beautiful thing I realized - <strong>every 3D object follows the same pattern</strong>:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> geometry = <span class="hljs-keyword">new</span> THREE.SomeGeometry(parameters);
<span class="hljs-keyword">const</span> material = <span class="hljs-keyword">new</span> THREE.SomeMaterial({ properties });
<span class="hljs-keyword">const</span> mesh = <span class="hljs-keyword">new</span> THREE.Mesh(geometry, material);
scene.add(mesh);
</code></pre>
<p>Whether it's a built-in sphere or a custom diamond shape, the workflow is identical.</p>
<h2 id="heading-what-i-learned">What I Learned</h2>
<h3 id="heading-1-geometry-is-just-data">1. <strong>Geometry is Just Data</strong></h3>
<p>A geometry is really just a bunch of numbers - coordinates for vertices (points) and how they connect to form triangles. Three.js provides built-in geometries so we don't have to calculate these by hand!</p>
<h3 id="heading-2-segments-control-smoothness">2. <strong>Segments Control Smoothness</strong></h3>
<p>All those "segments" parameters? They control how many triangles make up the shape. More triangles = smoother curves, but worse performance. It's always a trade-off!</p>
<h3 id="heading-3-every-shape-has-its-use-case">3. <strong>Every Shape Has Its Use Case</strong></h3>
<ul>
<li><p><strong>Spheres</strong>: Organic, round objects (planets, balls, bubbles)</p>
</li>
<li><p><strong>Cylinders</strong>: Structural elements (pillars, cans, tubes)</p>
</li>
<li><p><strong>Cones</strong>: Directional indicators (arrows, mountains, party hats)</p>
</li>
<li><p><strong>Torus</strong>: Circular objects with holes (rings, hoops, tires)</p>
</li>
</ul>
<h3 id="heading-4-material-properties-make-it-real">4. <strong>Material Properties Make It Real</strong></h3>
<p>The same geometry looks entirely different with different materials. The <code>metalness</code> and <code>roughness</code> properties in <code>MeshStandardMaterial</code> are especially powerful for creating realistic surfaces.</p>
<h3 id="heading-5-the-mesh-pattern-is-universal">5. <strong>The Mesh Pattern is Universal</strong></h3>
<p>Whether it's a sphere or torus, the workflow is always the same. This consistency makes Three.js easy to learn!</p>
<h3 id="heading-6-positioning-is-key">6. <strong>Positioning is Key</strong></h3>
<p>Using <code>position.set(x, y, z)</code> to arrange objects in your scene is crucial. I arranged mine in a row, but you can create any layout you want - stacks, circles, grids, whatever!</p>
<h2 id="heading-the-key-takeaway"><strong>The key takeaway?</strong></h2>
<p><strong>All geometries are just different shapes, but they work the same way.</strong> Once you understand that pattern, you can create anything!</p>
<p>Try building this gallery yourself and experiment with:</p>
<ul>
<li><p>Changing segment counts to see the smoothness difference</p>
</li>
<li><p>Mixing different materials on the same geometries</p>
</li>
<li><p>Positioning shapes in interesting arrangements (maybe a pyramid or circle pattern?)</p>
</li>
<li><p>Animating them in different ways</p>
</li>
</ul>
<p>Happy experimenting. 🎨</p>
]]></content:encoded></item><item><title><![CDATA[My First Three.js Scene: Building a 3D World in the Browser]]></title><description><![CDATA[Hey there! 👋 So I recently started my journey into the world of Three.js, and I wanted to document what I learned while creating my first 3D scene. If you know JavaScript or React but 3D graphics feels like magic, you're in the right place!
What is ...]]></description><link>https://blog.iamdipankarpaul.com/my-first-threejs-scene-building-a-3d-world-in-the-browser</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/my-first-threejs-scene-building-a-3d-world-in-the-browser</guid><category><![CDATA[ThreeJS]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Fri, 02 Jan 2026 10:33:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767357928862/42e75325-89cc-4826-a7b1-4cb3536a8bdc.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey there! 👋 So I recently started my journey into the world of Three.js, and I wanted to document what I learned while creating my first 3D scene. If you know JavaScript or React but 3D graphics feels like magic, you're in the right place!</p>
<h2 id="heading-what-is-threejs-anyway">What is Three.js Anyway?</h2>
<p>Three.js is a JavaScript library that makes it easier to create and display 3D graphics in the browser using WebGL. Without Three.js, you'd have to write complex WebGL code. With Three.js, you can create 3D scenes with just a few lines of JavaScript!</p>
<h2 id="heading-the-movie-set-analogy">The Movie Set Analogy</h2>
<p>Before diving into code, let me share an analogy that helped me understand Three.js concepts:</p>
<p><strong>Think of Three.js like creating a movie:</strong></p>
<ul>
<li><p><strong>Scene</strong> - The movie set where everything happens</p>
</li>
<li><p><strong>Camera</strong> - What the audience sees (their perspective)</p>
</li>
<li><p><strong>Objects</strong> - Actors and props on the set</p>
</li>
<li><p><strong>Lights</strong> - Studio lighting to make things visible</p>
</li>
<li><p><strong>Renderer</strong> - The camera crew that captures and displays everything</p>
</li>
</ul>
<p>Now let's build our first scene!</p>
<h2 id="heading-setting-up-the-html-canvas">Setting Up: The HTML Canvas</h2>
<p>First, we need a canvas element in our HTML where Three.js will draw our 3D scene:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>My First Three.js Scene<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
        <span class="hljs-selector-tag">body</span> { <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>; <span class="hljs-attribute">overflow</span>: hidden; }
        <span class="hljs-selector-tag">canvas</span> { <span class="hljs-attribute">display</span>: block; }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"canvas"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"script.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h2 id="heading-step-1-creating-the-scene">Step 1: Creating the Scene</h2>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"canvas"</span>);

<span class="hljs-comment">// 1. Create the Scene - This is our movie set</span>
<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
scene.background = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-number">0xbbbbbb</span>); <span class="hljs-comment">// Light gray background</span>
</code></pre>
<p>The scene is like an empty room or stage. Right now it's empty, but we'll add objects, lights, and a camera to it. The background color is like painting the walls of your room.</p>
<h2 id="heading-step-2-setting-up-the-camera">Step 2: Setting Up the Camera</h2>
<pre><code class="lang-javascript"><span class="hljs-comment">// 2. Create the Camera - This is the audience's viewpoint</span>
<span class="hljs-keyword">const</span> w = <span class="hljs-built_in">window</span>.innerWidth;
<span class="hljs-keyword">const</span> h = <span class="hljs-built_in">window</span>.innerHeight;

<span class="hljs-comment">// PerspectiveCamera mimics how human eyes see the world</span>
<span class="hljs-comment">// Parameters: field of view, aspect ratio, near clipping, far clipping</span>
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(<span class="hljs-number">75</span>, w / h, <span class="hljs-number">0.1</span>, <span class="hljs-number">1000</span>);
camera.position.z = <span class="hljs-number">5</span>; <span class="hljs-comment">// Move camera back so we can see objects</span>
</code></pre>
<p>Imagine you're holding a camera. The <code>field of view (75)</code> is how wide your lens is - a wider angle sees more. The <code>aspect ratio (w/h)</code> ensures things don't look stretched. The <code>near (0.1)</code> and <code>far (1000)</code> values are like how close and how far you can see clearly.</p>
<p>The <code>position.z = 5</code> moves our camera backward. In Three.js, the default coordinate system has:</p>
<ul>
<li><p>X-axis: left (-) to right (+)</p>
</li>
<li><p>Y-axis: down (-) to up (+)</p>
</li>
<li><p>Z-axis: into screen (-) to out toward you (+)</p>
</li>
</ul>
<h2 id="heading-step-3-creating-geometries-shapes">Step 3: Creating Geometries (Shapes)</h2>
<pre><code class="lang-javascript"><span class="hljs-comment">// 3. Create Geometries - The basic shapes of our objects</span>
<span class="hljs-keyword">const</span> icosahedronGeometry = <span class="hljs-keyword">new</span> THREE.IcosahedronGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">0</span>);
<span class="hljs-comment">// Parameters: radius (1), detail level (0 = low poly)</span>

<span class="hljs-keyword">const</span> boxGeometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">2</span>, <span class="hljs-number">0.1</span>, <span class="hljs-number">2</span>);
<span class="hljs-comment">// Parameters: width (2), height (0.1), depth (2)</span>
</code></pre>
<p>Geometry is like the skeleton or frame of an object. Think of it as a wireframe structure before you add color or texture. An icosahedron is a 20-sided dice shape, and a box is... well, a box!</p>
<h2 id="heading-step-4-creating-materials-appearance">Step 4: Creating Materials (Appearance)</h2>
<pre><code class="lang-javascript"><span class="hljs-comment">// 4. Create Materials - How our objects look (color, shininess, etc.)</span>
<span class="hljs-keyword">const</span> icosahedronMaterial = <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ 
    <span class="hljs-attr">color</span>: <span class="hljs-number">0x00ff00</span>,      <span class="hljs-comment">// Green color</span>
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0x00aa00</span>    <span class="hljs-comment">// Slight green glow</span>
});

<span class="hljs-keyword">const</span> boxMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ 
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xff0000</span>,      <span class="hljs-comment">// Red color</span>
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0xaa0000</span>    <span class="hljs-comment">// Slight red glow</span>
});
</code></pre>
<p>If geometry is the skeleton, material is the skin, paint, and texture. It defines whether something is shiny, matte, colorful, or transparent. The <code>emissive</code> property makes objects glow slightly, like they have their own light.</p>
<p><strong>Note:</strong> <code>MeshLambertMaterial</code> and <code>MeshStandardMaterial</code> require lights to be visible. Without lights, they'd appear black!</p>
<h2 id="heading-step-5-creating-meshes-combining-shape-appearance">Step 5: Creating Meshes (Combining Shape + Appearance)</h2>
<pre><code class="lang-javascript"><span class="hljs-comment">// 5. Create Meshes - Combine geometry + material</span>
<span class="hljs-keyword">const</span> icosahedron = <span class="hljs-keyword">new</span> THREE.Mesh(icosahedronGeometry, icosahedronMaterial);
<span class="hljs-keyword">const</span> box = <span class="hljs-keyword">new</span> THREE.Mesh(boxGeometry, boxMaterial);

<span class="hljs-comment">// Position the box below the icosahedron</span>
box.position.y = <span class="hljs-number">-1.5</span>;

<span class="hljs-comment">// Add both objects to our scene</span>
scene.add(icosahedron);
scene.add(box);
</code></pre>
<p>A mesh is the final "actor" on our set. It's the geometry (skeleton) wearing the material (skin/clothes). Now our objects exist in the scene and are ready to be seen!</p>
<h2 id="heading-step-6-let-there-be-light">Step 6: Let There Be Light!</h2>
<pre><code class="lang-javascript"><span class="hljs-comment">// 6. Add Lighting - Without this, we'd see nothing!</span>
<span class="hljs-keyword">const</span> spotLight = <span class="hljs-keyword">new</span> THREE.SpotLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">100</span>);
<span class="hljs-comment">// Parameters: color (white), intensity (100)</span>

spotLight.position.set(<span class="hljs-number">10</span>, <span class="hljs-number">10</span>, <span class="hljs-number">10</span>);
<span class="hljs-comment">// Position it above and to the side</span>

scene.add(spotLight);
</code></pre>
<p>Just like a movie set needs studio lights, our 3D scene needs lights to illuminate objects. Without lights, materials like <code>MeshLambertMaterial</code> would appear completely black because they depend on light to show their colors.</p>
<h2 id="heading-step-7-the-renderer-bringing-it-all-together">Step 7: The Renderer - Bringing It All Together</h2>
<pre><code class="lang-javascript"><span class="hljs-comment">// 7. Create the Renderer - This displays everything on screen</span>
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ <span class="hljs-attr">canvas</span>: canvas });

<span class="hljs-comment">// Use device pixel ratio for sharper rendering on high-DPI screens</span>
renderer.setPixelRatio(<span class="hljs-built_in">window</span>.devicePixelRatio);

<span class="hljs-comment">// Set renderer size to fill the window</span>
renderer.setSize(w, h);
</code></pre>
<p>The renderer is like the film crew that captures everything and projects it onto the screen. It takes the scene, camera view, and all objects, then draws them on the canvas element.</p>
<h2 id="heading-step-8-the-animation-loop-making-things-move">Step 8: The Animation Loop - Making Things Move!</h2>
<p>Here's where the magic happens. We need to continuously render our scene to create animation:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// 8. Animation Loop - Re-render the scene every frame</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animateScene</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Request the browser to call this function before the next repaint</span>
    <span class="hljs-built_in">window</span>.requestAnimationFrame(animateScene);

    <span class="hljs-comment">// Rotate the icosahedron on X and Y axes</span>
    icosahedron.rotation.x += <span class="hljs-number">0.01</span>;
    icosahedron.rotation.y += <span class="hljs-number">0.01</span>;

    <span class="hljs-comment">// Rotate the box slower on Y axis only</span>
    box.rotation.y += <span class="hljs-number">0.005</span>;

    <span class="hljs-comment">// Render the scene from the camera's perspective</span>
    renderer.render(scene, camera);
}

<span class="hljs-comment">// Start the animation loop</span>
animateScene();
</code></pre>
<p><strong>Why do we need an animation loop?</strong></p>
<p>Think about how movies work - they're just a series of still images shown rapidly (typically 24–60 frames per second). Our animation loop does the same thing:</p>
<ol>
<li><p><strong>Update</strong> - Change object positions, rotations, or properties</p>
</li>
<li><p><strong>Render</strong> - Draw the updated scene</p>
</li>
<li><p><strong>Repeat</strong> - Do it again for the next frame</p>
</li>
</ol>
<p><code>requestAnimationFrame</code> is the browser's built-in way to say "call this function right before you redraw the screen" - typically 60 times per second. This creates smooth animations!</p>
<p>Without this loop, we'd just see a single static frame. With it, our objects rotate smoothly.</p>
<h2 id="heading-step-9-responsive-design-handle-window-resizing">Step 9: Responsive Design - Handle Window Resizing</h2>
<pre><code class="lang-javascript"><span class="hljs-comment">// 9. Handle window resize events</span>
<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"resize"</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> w = <span class="hljs-built_in">window</span>.innerWidth;
    <span class="hljs-keyword">const</span> h = <span class="hljs-built_in">window</span>.innerHeight;

    <span class="hljs-comment">// Update camera aspect ratio</span>
    camera.aspect = w / h;
    camera.updateProjectionMatrix(); <span class="hljs-comment">// Must call this after changing aspect</span>

    <span class="hljs-comment">// Update renderer size</span>
    renderer.setSize(w, h);
});
</code></pre>
<p><strong>Why is this important?</strong></p>
<p>When the window resizes, our canvas and camera need to adjust. Without this, your 3D scene would look stretched or squished. The <code>updateProjectionMatrix()</code> call is crucial - it recalculates how the camera projects 3D space onto 2D screen space.</p>
<h2 id="heading-the-complete-code">The Complete Code</h2>
<p>Here's everything together:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> THREE <span class="hljs-keyword">from</span> <span class="hljs-string">"three"</span>;

<span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"canvas"</span>);

<span class="hljs-comment">// 1. Scene - The movie set</span>
<span class="hljs-keyword">const</span> scene = <span class="hljs-keyword">new</span> THREE.Scene();
scene.background = <span class="hljs-keyword">new</span> THREE.Color(<span class="hljs-number">0xbbbbbb</span>);

<span class="hljs-comment">// 2. Camera - The audience's viewpoint</span>
<span class="hljs-keyword">const</span> w = <span class="hljs-built_in">window</span>.innerWidth;
<span class="hljs-keyword">const</span> h = <span class="hljs-built_in">window</span>.innerHeight;
<span class="hljs-keyword">const</span> camera = <span class="hljs-keyword">new</span> THREE.PerspectiveCamera(<span class="hljs-number">75</span>, w / h, <span class="hljs-number">0.1</span>, <span class="hljs-number">1000</span>);
camera.position.z = <span class="hljs-number">5</span>;

<span class="hljs-comment">// 3. Geometries - Basic shapes</span>
<span class="hljs-keyword">const</span> icosahedronGeometry = <span class="hljs-keyword">new</span> THREE.IcosahedronGeometry(<span class="hljs-number">1</span>, <span class="hljs-number">0</span>);
<span class="hljs-keyword">const</span> boxGeometry = <span class="hljs-keyword">new</span> THREE.BoxGeometry(<span class="hljs-number">2</span>, <span class="hljs-number">0.1</span>, <span class="hljs-number">2</span>);

<span class="hljs-comment">// 4. Materials - How objects look</span>
<span class="hljs-keyword">const</span> icosahedronMaterial = <span class="hljs-keyword">new</span> THREE.MeshLambertMaterial({ 
    <span class="hljs-attr">color</span>: <span class="hljs-number">0x00ff00</span>, 
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0x00aa00</span> 
});
<span class="hljs-keyword">const</span> boxMaterial = <span class="hljs-keyword">new</span> THREE.MeshStandardMaterial({ 
    <span class="hljs-attr">color</span>: <span class="hljs-number">0xff0000</span>, 
    <span class="hljs-attr">emissive</span>: <span class="hljs-number">0xaa0000</span> 
});

<span class="hljs-comment">// 5. Meshes - Combine geometry + material</span>
<span class="hljs-keyword">const</span> icosahedron = <span class="hljs-keyword">new</span> THREE.Mesh(icosahedronGeometry, icosahedronMaterial);
<span class="hljs-keyword">const</span> box = <span class="hljs-keyword">new</span> THREE.Mesh(boxGeometry, boxMaterial);
box.position.y = <span class="hljs-number">-1.5</span>;

scene.add(icosahedron);
scene.add(box);

<span class="hljs-comment">// 6. Lighting - Make objects visible</span>
<span class="hljs-keyword">const</span> spotLight = <span class="hljs-keyword">new</span> THREE.SpotLight(<span class="hljs-number">0xffffff</span>, <span class="hljs-number">100</span>);
spotLight.position.set(<span class="hljs-number">10</span>, <span class="hljs-number">10</span>, <span class="hljs-number">10</span>);
scene.add(spotLight);

<span class="hljs-comment">// 7. Renderer - Display everything</span>
<span class="hljs-keyword">const</span> renderer = <span class="hljs-keyword">new</span> THREE.WebGLRenderer({ <span class="hljs-attr">canvas</span>: canvas });
renderer.setPixelRatio(<span class="hljs-built_in">window</span>.devicePixelRatio);
renderer.setSize(w, h);

<span class="hljs-comment">// 8. Animation Loop - Create movement</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">animateScene</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">window</span>.requestAnimationFrame(animateScene);

    icosahedron.rotation.x += <span class="hljs-number">0.01</span>;
    icosahedron.rotation.y += <span class="hljs-number">0.01</span>;
    box.rotation.y += <span class="hljs-number">0.005</span>;

    renderer.render(scene, camera);
}

animateScene();

<span class="hljs-comment">// 9. Handle window resize</span>
<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"resize"</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> w = <span class="hljs-built_in">window</span>.innerWidth;
    <span class="hljs-keyword">const</span> h = <span class="hljs-built_in">window</span>.innerHeight;

    camera.aspect = w / h;
    camera.updateProjectionMatrix();
    renderer.setSize(w, h);
});
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767357896051/a6ea44cf-0cf4-4832-9583-66eb2e3b0784.gif" alt class="image--center mx-auto" /></p>
<h2 id="heading-what-i-learned">What I Learned</h2>
<h3 id="heading-1-the-threejs-pattern-is-consistent">1. <strong>The Three.js Pattern is Consistent</strong></h3>
<p>Every Three.js project follows this pattern:</p>
<ul>
<li><p>Create a scene</p>
</li>
<li><p>Set up a camera</p>
</li>
<li><p>Create objects (geometry + material = mesh)</p>
</li>
<li><p>Add lights</p>
</li>
<li><p>Create a renderer</p>
</li>
<li><p>Animate with a render loop</p>
</li>
</ul>
<p>Once you understand this flow, you can build any 3D scene!</p>
<h3 id="heading-2-the-animation-loop-is-the-heart">2. <strong>The Animation Loop is the Heart</strong></h3>
<p>The <code>requestAnimationFrame</code> loop is what brings everything to life. Without it, you just have a static image. This is where you:</p>
<ul>
<li><p>Update object properties (position, rotation, scale)</p>
</li>
<li><p>Handle user interactions</p>
</li>
<li><p>Render the updated scene</p>
</li>
</ul>
<h3 id="heading-3-coordinates-take-practice">3. <strong>Coordinates Take Practice</strong></h3>
<p>The 3D coordinate system (X, Y, Z) takes time to internalize. Remember:</p>
<ul>
<li><p>Positive Z comes toward you (out of screen)</p>
</li>
<li><p>Positive Y goes up</p>
</li>
<li><p>Positive X goes right</p>
</li>
</ul>
<h3 id="heading-4-materials-need-lights">4. <strong>Materials Need Lights</strong></h3>
<p>This caught me initially! Materials like <code>MeshLambertMaterial</code> and <code>MeshStandardMaterial</code> won't show up without lights. If you want objects visible without lights, use <code>MeshBasicMaterial</code>.</p>
<h3 id="heading-5-small-values-create-smooth-animation">5. <strong>Small Values Create Smooth Animation</strong></h3>
<p>Notice the rotation values are tiny (0.01, 0.005). That's because the animation loop runs ~60 times per second. Small increments add up to smooth, visible movement!</p>
<h3 id="heading-6-window-resizing-matters">6. <strong>Window Resizing Matters</strong></h3>
<p>Always handle resize events to keep your scene looking good on all screen sizes. Don't forget <code>updateProjectionMatrix()</code> after changing the camera's aspect ratio!</p>
<p><strong>If you're starting your Three.js journey too, I'd recommend:</strong></p>
<ol>
<li><p>Build this basic scene yourself</p>
</li>
<li><p>Experiment by changing values (colors, positions, rotation speeds)</p>
</li>
<li><p>Add new shapes and lights</p>
</li>
<li><p>Break things and fix them - that's how you learn!</p>
</li>
</ol>
<p>The Three.js documentation is really helpful, so don't hesitate to explore it as you build.</p>
<p>Happy coding! 🚀</p>
<hr />
<p><em>This is part of my learning journey, documenting my exploration of Three.js and 3D web graphics. Follow along as I build toward creating my own 3D portfolio website!</em></p>
]]></content:encoded></item><item><title><![CDATA[How I Connected My Custom Subdomain to Hashnode in Minutes (Step-by-Step Guide)]]></title><description><![CDATA[Having a developer blog is a great way to document your learning journey, share solutions, and grow your personal brand. Recently, I connected my custom subdomain, blog.iamdipankarpaul.com, to my Hashnode blog. In this article, I’ll walk you through ...]]></description><link>https://blog.iamdipankarpaul.com/how-i-connected-my-custom-subdomain-to-hashnode-in-minutes-step-by-step-guide</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/how-i-connected-my-custom-subdomain-to-hashnode-in-minutes-step-by-step-guide</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Custom Domain]]></category><category><![CDATA[Blogging]]></category><category><![CDATA[domain]]></category><category><![CDATA[branding]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Thu, 03 Jul 2025 12:05:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1751545974283/957273e4-fb5b-4053-855a-571234bcdd3a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Having a developer blog is a great way to document your learning journey, share solutions, and grow your personal brand. Recently, I connected my custom subdomain, <a target="_blank" href="https://blog.iamdipankarpaul.com/"><strong>blog.iamdipankarpaul.com</strong></a>, to my <a target="_blank" href="https://hashnode.com/">Hashnode</a> blog. In this article, I’ll walk you through why I chose Hashnode, why linking a custom subdomain matters, and how you can set it up for your own blog.</p>
<h2 id="heading-why-i-chose-hashnode-for-my-developer-blog">Why I Chose Hashnode for My Developer Blog</h2>
<p>As a developer, I was looking for a fast blogging platform, focused on tech content, SEO-friendly, and developer-centric. After trying out a few options, <strong>Hashnode</strong> stood out for several reasons:</p>
<ul>
<li><p><strong>Developer-Focused Community</strong>: Hashnode is built specifically for developers. You write for people who understand your content and value your insights.</p>
</li>
<li><p><strong>Fast and Minimal</strong>: Clean writing experience, blazing-fast performance.</p>
</li>
<li><p><strong>Free Custom Domain Support</strong>: Unlike many platforms, Hashnode lets you use your own domain or subdomain for free.</p>
</li>
<li><p><strong>SEO and Analytics Ready</strong>: Out-of-the-box SEO optimization and traffic analytics without the need for third-party plugins.</p>
</li>
</ul>
<p>If you’re a developer or a tech enthusiast, Hashnode is a solid choice for hosting your blog.</p>
<h2 id="heading-why-connect-a-custom-subdomain">Why Connect a Custom Subdomain?</h2>
<p>By default, Hashnode gives you a <code>yourblog.hashnode.dev</code> address. But using your own domain or subdomain has many advantages:</p>
<ul>
<li><p><strong>Branding</strong>: Your domain reflects your identity (like <code>blog.iamdipankarpaul.com</code>), helping build a professional image.</p>
</li>
<li><p><strong>Trust and Credibility</strong>: A custom domain looks more polished and trustworthy.</p>
</li>
<li><p><strong>SEO Benefits</strong>: Linking your subdomain helps drive traffic to your primary domain and keeps your digital presence consistent.</p>
</li>
<li><p><strong>Future-Proofing</strong>: If you ever move platforms, you can keep the same URL structure without affecting SEO or reader access.</p>
</li>
</ul>
<p>Now that you know <strong><em>why</em></strong>, let me show you <strong><em>how</em></strong> to do it.</p>
<h2 id="heading-how-to-connect-a-custom-subdomain-to-your-hashnode-blog">How to Connect a Custom Subdomain to Your Hashnode Blog</h2>
<p>Here’s a step-by-step guide I followed to point my subdomain <code>blog.iamdipankarpaul.com</code> to my Hashnode blog.</p>
<h3 id="heading-1-configure-your-subdomain-in-hashnode">1. Configure Your Subdomain in Hashnode</h3>
<ol>
<li><p>Log in to your <strong>Hashnode</strong> account.</p>
</li>
<li><p>Click your profile picture (top right) and choose <strong>"Manage your blogs"</strong>.</p>
</li>
<li><p>Select the blog you want to set up, and go to its <strong>Dashboard</strong>.</p>
</li>
<li><p>Click the <strong>Domain</strong> tab on the left sidebar.</p>
</li>
<li><p>Under the <strong>Custom Domain</strong> section, enter your subdomain (e.g., <code>blog.iamdipankarpaul.com</code>).<br /> <strong>Note</strong>: Don’t include <code>www</code> or <code>https://</code>.</p>
</li>
<li><p>Click <strong>Update</strong> to save.</p>
</li>
</ol>
<h3 id="heading-2-update-dns-records-in-your-domain-provider">2. Update DNS Records in Your Domain Provider</h3>
<p>Now, you’ll need to configure your domain’s DNS settings.</p>
<ol>
<li><p>Log in to your <strong>domain registrar</strong> (e.g., Namecheap, GoDaddy, Cloudflare, Hostinger).</p>
</li>
<li><p>Navigate to the <strong>DNS Management</strong> or <strong>DNS Editor</strong> section.</p>
</li>
<li><p>Add a new <strong>CNAME</strong> record with these details:</p>
<pre><code class="lang-plaintext"> Name/Host:  blog
 Type:       CNAME
 Value:      hashnode.network
 TTL:        Default or 1 hour
</code></pre>
<p> Don’t worry Hashnode will provide all these details.</p>
</li>
<li><p>Save the DNS record.</p>
</li>
</ol>
<p>Be cautious with the record type, ensure it’s <strong>CNAME</strong> and not A or TXT.</p>
<h3 id="heading-3-wait-for-dns-propagation">3. Wait for DNS Propagation</h3>
<p>DNS updates typically take <strong>a few minutes to 24 hours</strong> to fully propagate.</p>
<p>Hashnode will automatically detect the changes and provision an <strong>SSL certificate</strong> once the setup is successful. You’ll have a secure <code>https://blog.yourdomain.com</code> connection without extra steps.</p>
<h3 id="heading-4-verify-the-connection-on-hashnode">4. Verify the Connection on Hashnode</h3>
<ol>
<li><p>Go back to your blog’s <strong>Domain</strong> tab in the Hashnode dashboard.</p>
</li>
<li><p>You’ll see a green checkmark or success message once the domain is correctly mapped.</p>
</li>
</ol>
<p>If you see an error or it’s taking too long, double-check your DNS settings or wait a bit longer (at least <strong>24 hours)</strong>.</p>
<h2 id="heading-final-notes-and-tips">Final Notes and Tips</h2>
<ul>
<li><p>Want a different subdomain like <code>journal.yourdomain.com</code> or <code>notes.yourdomain.com</code>? Just update the DNS and Hashnode settings with your chosen subdomain.</p>
</li>
<li><p>Facing issues? Reach out to Hashnode’s support or double-check your DNS provider’s settings.</p>
</li>
<li><p>This process works with most domain registrars, including Namecheap, GoDaddy, Google Domains, Hostinger, and Cloudflare.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Setting up a custom subdomain for your Hashnode blog is simple, free, and incredibly beneficial for branding and long-term consistency. If you’re serious about blogging as a developer, take a few minutes to connect your domain; it’s worth it.</p>
<p>If you’ve followed this guide and found it helpful, feel free to share it!</p>
]]></content:encoded></item><item><title><![CDATA[How Rust Manages Memory Without a Garbage Collector]]></title><description><![CDATA[Introduction
Memory management is a fundamental part of programming, and different languages handle it in different ways. Many modern languages, such as Java, Python, and JavaScript, use a Garbage Collector (GC) to automatically free up memory that i...]]></description><link>https://blog.iamdipankarpaul.com/how-rust-manages-memory-without-a-garbage-collector</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/how-rust-manages-memory-without-a-garbage-collector</guid><category><![CDATA[Rust]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[memory-management]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[Performance Optimization]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Wed, 30 Apr 2025 18:30:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1749276191191/189ad7f9-58ed-40a7-86c8-3c9d4ce85c0b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction"><strong>Introduction</strong></h2>
<p>Memory management is a fundamental part of programming, and different languages handle it in different ways. Many modern languages, such as Java, Python, and JavaScript, use a <strong>Garbage Collector (GC)</strong> to automatically free up memory that is no longer needed. While this approach simplifies development, it can lead to <strong>performance issues</strong> due to unpredictable pauses when the GC runs.</p>
<p>Rust takes a completely different approach. It <strong>manages memory without using a garbage collector</strong>, ensuring both <strong>memory safety and high performance</strong>. It does this through an innovative system of <strong>ownership, borrowing, and lifetimes</strong>. If you’ve ever wondered how Rust achieves this, this article will explain everything in a simple and beginner-friendly way.</p>
<h2 id="heading-how-rust-handles-memory-management"><strong>How Rust Handles Memory Management</strong></h2>
<p>Instead of relying on a background garbage collector, Rust enforces <strong>strict memory rules at compile time</strong>. This eliminates common memory issues like <strong>null pointer dereferencing, memory leaks, and data races</strong>.</p>
<p>Rust achieves this using three key concepts:</p>
<ol>
<li><p><strong>Ownership</strong> - Determines who is responsible for a value and when it should be deallocated.</p>
</li>
<li><p><strong>Borrowing &amp; References</strong> - Allows multiple parts of a program to use data without unnecessary copying.</p>
</li>
<li><p><strong>Lifetimes</strong> - Ensures references are always valid, preventing dangling pointers.</p>
</li>
</ol>
<p>Let’s break down each concept with examples.</p>
<h2 id="heading-1-ownership-the-foundation-of-rusts-memory-management"><strong>1. Ownership: The Foundation of Rust’s Memory Management</strong></h2>
<p>In Rust, <strong>every value has a single owner</strong>, meaning only one variable can own a piece of data at any time. When the owner goes out of scope, Rust automatically <strong>frees the memory</strong>.</p>
<h3 id="heading-example-ownership-in-action"><strong>Example: Ownership in Action</strong></h3>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> s = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Hello, Rust!"</span>); <span class="hljs-comment">// 's' owns the memory</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, s); <span class="hljs-comment">// Valid use of s</span>
} <span class="hljs-comment">// 's' goes out of scope, memory is freed automatically</span>
</code></pre>
<p>In this example, once <code>s</code> goes out of scope at the end of <code>main()</code>, Rust <strong>automatically deallocates</strong> the memory used by the string. There’s no need for a <code>free()</code> function like in C or C++.</p>
<h3 id="heading-ownership-rules"><strong>Ownership Rules</strong></h3>
<ol>
<li><p><strong>Each value has a single owner.</strong></p>
</li>
<li><p><strong>When the owner goes out of scope, the value is dropped.</strong></p>
</li>
<li><p><strong>Values can be transferred (moved), but not copied unless explicitly allowed.</strong></p>
</li>
</ol>
<h3 id="heading-move-semantics-preventing-double-free-errors"><strong>Move Semantics: Preventing Double Free Errors</strong></h3>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> s1 = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Rust"</span>);
    <span class="hljs-keyword">let</span> s2 = s1; <span class="hljs-comment">// Ownership moves from s1 to s2</span>

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, s1); <span class="hljs-comment">// ❌ ERROR: s1 is no longer valid</span>
}
</code></pre>
<p>When <code>s1</code> is assigned to <code>s2</code>, the ownership of the string moves to <code>s2</code>, and <code>s1</code> is no longer valid. This prevents <strong>double free errors</strong>, where two variables try to free the same memory.</p>
<p>If you want to <strong>clone</strong> the data instead of transferring ownership, you must explicitly call <code>.clone()</code>, which creates a new copy of the data:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> s1 = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Rust"</span>);
    <span class="hljs-keyword">let</span> s2 = s1.clone(); <span class="hljs-comment">// Creates a separate copy</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, s1); <span class="hljs-comment">// ✅ No error, s1 is still valid</span>
}
</code></pre>
<h2 id="heading-2-borrowing-amp-references-using-data-without-taking-ownership"><strong>2. Borrowing &amp; References: Using Data Without Taking Ownership</strong></h2>
<p>Sometimes, you want to use a value <strong>without taking ownership</strong>. Rust allows this through <strong>borrowing</strong> using references (<code>&amp;T</code> for immutable references, <code>&amp;mut T</code> for mutable references).</p>
<h3 id="heading-immutable-borrowing-ampt-read-only-multiple-allowed"><strong>Immutable Borrowing (</strong><code>&amp;T</code> - Read Only, Multiple Allowed)</h3>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">print_length</span></span>(s: &amp;<span class="hljs-built_in">String</span>) {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Length: {}"</span>, s.len());
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> s = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Rust"</span>);
    print_length(&amp;s); <span class="hljs-comment">// Borrowing s, ownership not transferred</span>
    print_length(&amp;s); <span class="hljs-comment">// ✅ Can borrow multiple times</span>
}
</code></pre>
<p>Since <code>print_length</code> only <strong>reads</strong> the string, it can safely borrow it multiple times.</p>
<h3 id="heading-mutable-borrowing-ampmut-t-read-amp-write-only-one-allowed-at-a-time"><strong>Mutable Borrowing (</strong><code>&amp;mut T</code> - Read &amp; Write, Only One Allowed at a Time)</h3>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">make_uppercase</span></span>(s: &amp;<span class="hljs-keyword">mut</span> <span class="hljs-built_in">String</span>) {
    s.push_str(<span class="hljs-string">" Rocks!"</span>);
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> s = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Rust"</span>);
    make_uppercase(&amp;<span class="hljs-keyword">mut</span> s); <span class="hljs-comment">// Borrowing mutably</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, s); <span class="hljs-comment">// ✅ Modified string</span>
}
</code></pre>
<p>Rust <strong>prevents multiple mutable borrows at the same time</strong>, avoiding <strong>data races</strong> that occur in multi-threaded programs.</p>
<h4 id="heading-borrowing-rules"><strong>Borrowing Rules</strong></h4>
<ol>
<li><p><strong>Multiple immutable references (</strong><code>&amp;T</code>) are allowed at the same time.</p>
</li>
<li><p><strong>Only one mutable reference (</strong><code>&amp;mut T</code>) is allowed at a time.</p>
</li>
<li><p><strong>A variable cannot have both immutable and mutable references simultaneously.</strong></p>
</li>
</ol>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> s = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Rust"</span>);

    <span class="hljs-keyword">let</span> r1 = &amp;s;
    <span class="hljs-keyword">let</span> r2 = &amp;s;
    <span class="hljs-keyword">let</span> r3 = &amp;<span class="hljs-keyword">mut</span> s; <span class="hljs-comment">// ❌ ERROR: Cannot have a mutable reference while immutable ones exist</span>

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}, {}"</span>, r1, r2);
}
</code></pre>
<h2 id="heading-3-lifetimes-ensuring-references-are-always-valid"><strong>3. Lifetimes: Ensuring References Are Always Valid</strong></h2>
<p>Borrowing helps avoid unnecessary copies, but it introduces another problem: <strong>Dangling References</strong> - when a reference outlives the data it points to. Rust prevents this using <strong>lifetimes</strong>.</p>
<h3 id="heading-example-of-a-dangling-reference-invalid-code"><strong>Example of a Dangling Reference (Invalid Code)</strong></h3>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">dangling_reference</span></span>() -&gt; &amp;<span class="hljs-built_in">String</span> {
    <span class="hljs-keyword">let</span> s = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Hello"</span>); <span class="hljs-comment">// 's' is created</span>
    &amp;s <span class="hljs-comment">// ❌ ERROR: Reference to 's' is returned, but 's' will be dropped</span>
} <span class="hljs-comment">// 's' is dropped here!</span>
</code></pre>
<p>Since <code>s</code> <strong>goes out of scope</strong> at the end of the function, returning a reference to it is unsafe.</p>
<h3 id="heading-lifetime-annotations-a"><strong>Lifetime Annotations (</strong><code>'a</code>)</h3>
<p>Rust uses <strong>lifetime annotations</strong> to ensure references are valid:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">longest</span></span>&lt;<span class="hljs-symbol">'a</span>&gt;(s1: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-built_in">str</span>, s2: &amp;<span class="hljs-symbol">'a</span> <span class="hljs-built_in">str</span>) -&gt; &amp;<span class="hljs-symbol">'a</span> <span class="hljs-built_in">str</span> {
    <span class="hljs-keyword">if</span> s1.len() &gt; s2.len() { s1 } <span class="hljs-keyword">else</span> { s2 }
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> string1 = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Rust"</span>);
    <span class="hljs-keyword">let</span> string2 = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Programming"</span>);

    <span class="hljs-keyword">let</span> result = longest(&amp;string1, &amp;string2);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Longest string: {}"</span>, result);
}
</code></pre>
<p>Here, <code>'a</code> ensures that both input references (<code>s1</code> and <code>s2</code>) and the returned reference <strong>live at least as long as each other</strong>.</p>
<h2 id="heading-why-rusts-approach-is-better-than-garbage-collection"><strong>Why Rust’s Approach is Better Than Garbage Collection?</strong></h2>
<p>✅ <strong>No Runtime Overhead</strong> – No background GC scanning, leading to faster performance.<br />✅ <strong>Predictable Performance</strong> – Memory is freed exactly when it’s no longer needed.<br />✅ <strong>No Memory Leaks</strong> – Rust enforces strict ownership rules, ensuring safe memory usage.<br />✅ <strong>No Null References</strong> – Rust eliminates null pointer errors, unlike languages with GC.</p>
<p>This makes Rust ideal for <strong>system programming, game development, and performance-critical applications</strong>.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Rust’s <strong>ownership, borrowing, and lifetimes</strong> system provides memory safety <strong>without needing a garbage collector</strong>. Unlike languages with automatic GC, Rust <strong>deallocates memory as soon as it’s no longer needed</strong>, preventing performance slowdowns. Compared to manual memory management in C/C++, Rust <strong>eliminates memory leaks and undefined behavior</strong> while ensuring high performance.</p>
<p>If you’re new to Rust, mastering <strong>ownership, borrowing, and lifetimes</strong> will help you unlock its full potential. This unique approach makes Rust an excellent choice for <strong>low-level programming, embedded systems, and high-performance applications</strong>.</p>
]]></content:encoded></item><item><title><![CDATA[TypeScript Utility Types: Making Your Code More Efficient]]></title><description><![CDATA[Introduction
TypeScript is a powerful tool for writing scalable and maintainable JavaScript applications. One of its best features is Utility Types, which help developers modify, filter, or transform types without rewriting them from scratch. These b...]]></description><link>https://blog.iamdipankarpaul.com/typescript-utility-types-making-your-code-more-efficient</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/typescript-utility-types-making-your-code-more-efficient</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[utility type]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Tue, 15 Apr 2025 18:30:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1749793854577/ddff3913-cf49-48c9-9910-dbe75e526490.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>TypeScript is a powerful tool for writing scalable and maintainable JavaScript applications. One of its best features is <strong>Utility Types</strong>, which help developers <strong>modify, filter, or transform types</strong> without rewriting them from scratch. These built-in types make code <strong>more reusable, readable, and error-proof</strong>.</p>
<p>If you’ve ever found yourself manually tweaking a type definition, TypeScript Utility Types can save you a lot of time! In this article, we’ll explore the most useful utility types and how they make coding in TypeScript easier and more efficient.</p>
<h2 id="heading-what-are-typescript-utility-types">What Are TypeScript Utility Types?</h2>
<p>Utility Types in TypeScript are <strong>predefined generic types</strong> that let you <strong>modify existing types</strong>. Instead of manually creating a new type, you can use these utilities to quickly create a variation of an existing type.</p>
<p>Think of them like <strong>shortcuts</strong> that help you avoid unnecessary duplication and make your code more flexible.</p>
<h2 id="heading-essential-typescript-utility-types">Essential TypeScript Utility Types</h2>
<p>Here are some of the most commonly used Utility Types with examples:</p>
<h3 id="heading-1-partial-makes-all-properties-optional"><strong>1.</strong> <code>Partial&lt;T&gt;</code> - Makes All Properties Optional</h3>
<p>This utility makes all properties of an object <strong>optional</strong>, allowing you to define only some values while keeping the original structure.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> User = {
  name: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
};

<span class="hljs-keyword">const</span> user: Partial&lt;User&gt; = { name: <span class="hljs-string">"John"</span> }; <span class="hljs-comment">// ✅ No error, 'age' is optional</span>
</code></pre>
<h3 id="heading-2-required-makes-all-properties-required"><strong>2.</strong> <code>Required&lt;T&gt;</code> - Makes All Properties Required</h3>
<p>Opposite to <code>Partial</code>, this utility makes all properties <strong>mandatory</strong>, even if they were originally optional.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> User = {
  name?: <span class="hljs-built_in">string</span>;
  age?: <span class="hljs-built_in">number</span>;
};

<span class="hljs-keyword">const</span> user: Required&lt;User&gt; = { name: <span class="hljs-string">"John"</span> }; <span class="hljs-comment">// ❌ Error if 'age' is missing</span>
</code></pre>
<h3 id="heading-3-readonly-prevents-modifications"><strong>3.</strong> <code>Readonly&lt;T&gt;</code> - Prevents Modifications</h3>
<p>This utility ensures that once an object is created, its properties <strong>cannot be changed</strong>.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> User = {
  name: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
};

<span class="hljs-keyword">const</span> user: Readonly&lt;User&gt; = { name: <span class="hljs-string">"John"</span>, age: <span class="hljs-number">25</span> };
user.name = <span class="hljs-string">"Doe"</span>; <span class="hljs-comment">// ❌ Error: Cannot modify readonly property</span>
</code></pre>
<h3 id="heading-4-pick-selects-specific-properties"><strong>4.</strong> <code>Pick&lt;T, K&gt;</code> - Selects Specific Properties</h3>
<p>This utility allows you to create a new type by selecting only <strong>certain properties</strong> from an existing type.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> User = {
  name: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
  email: <span class="hljs-built_in">string</span>;
};

<span class="hljs-keyword">type</span> NameOnly = Pick&lt;User, <span class="hljs-string">"name"</span>&gt;; <span class="hljs-comment">// { name: string }</span>
</code></pre>
<h3 id="heading-5-omit-removes-specific-properties"><strong>5.</strong> <code>Omit&lt;T, K&gt;</code> - Removes Specific Properties</h3>
<p>Similar to <code>Pick</code>, but instead of selecting, it <strong>removes</strong> specific properties from a type.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> User = {
  name: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
  email: <span class="hljs-built_in">string</span>;
};

<span class="hljs-keyword">type</span> NoEmail = Omit&lt;User, <span class="hljs-string">"email"</span>&gt;; <span class="hljs-comment">// { name: string; age: number }</span>
</code></pre>
<h2 id="heading-advanced-typescript-utility-types">Advanced TypeScript Utility Types</h2>
<h3 id="heading-6-record-creates-an-object-type-with-fixed-keys"><strong>6.</strong> <code>Record&lt;K, T&gt;</code> - Creates an Object Type with Fixed Keys</h3>
<p>This utility helps define an object <strong>with specific keys and a common value type</strong>.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> Roles = Record&lt;<span class="hljs-string">"admin"</span> | <span class="hljs-string">"user"</span> | <span class="hljs-string">"guest"</span>, <span class="hljs-built_in">string</span>&gt;;

<span class="hljs-keyword">const</span> roleNames: Roles = {
  admin: <span class="hljs-string">"Administrator"</span>,
  user: <span class="hljs-string">"Registered User"</span>,
  guest: <span class="hljs-string">"Visitor"</span>,
};
</code></pre>
<h3 id="heading-7-exclude-removes-specific-types-from-a-union"><strong>7.</strong> <code>Exclude&lt;T, U&gt;</code> - Removes Specific Types from a Union</h3>
<p>Used to remove one or more types from a <strong>union type</strong>.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> Status = <span class="hljs-string">"success"</span> | <span class="hljs-string">"error"</span> | <span class="hljs-string">"pending"</span>;
<span class="hljs-keyword">type</span> ActiveStatus = Exclude&lt;Status, <span class="hljs-string">"pending"</span>&gt;; <span class="hljs-comment">// "success" | "error"</span>
</code></pre>
<h3 id="heading-8-extract-keeps-only-specific-types-from-a-union"><strong>8.</strong> <code>Extract&lt;T, U&gt;</code> - Keeps Only Specific Types from a Union</h3>
<p>The opposite of <code>Exclude</code>, it picks only the matching types.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> Status = <span class="hljs-string">"success"</span> | <span class="hljs-string">"error"</span> | <span class="hljs-string">"pending"</span>;
<span class="hljs-keyword">type</span> ErrorStatus = Extract&lt;Status, <span class="hljs-string">"error"</span>&gt;; <span class="hljs-comment">// "error"</span>
</code></pre>
<h3 id="heading-9-nonnullable-removes-null-and-undefined"><strong>9.</strong> <code>NonNullable&lt;T&gt;</code> - Removes <code>null</code> and <code>undefined</code></h3>
<p>Ensures a type <strong>does not include</strong> <code>null</code> or <code>undefined</code>.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> Data = <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span> | <span class="hljs-literal">undefined</span>;
<span class="hljs-keyword">type</span> CleanData = NonNullable&lt;Data&gt;; <span class="hljs-comment">// string</span>
</code></pre>
<h3 id="heading-10-returntype-gets-the-return-type-of-a-function"><strong>10.</strong> <code>ReturnType&lt;T&gt;</code> - Gets the Return Type of a Function</h3>
<p>Extracts the return type from a function.</p>
<pre><code class="lang-ts"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAge</span>(<span class="hljs-params"></span>): <span class="hljs-title">number</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-number">25</span>; }
<span class="hljs-keyword">type</span> Age = ReturnType&lt;<span class="hljs-keyword">typeof</span> getAge&gt;; <span class="hljs-comment">// number</span>
</code></pre>
<h3 id="heading-11-parameters-gets-function-parameter-types"><strong>11.</strong> <code>Parameters&lt;T&gt;</code> - Gets Function Parameter Types</h3>
<p>Extracts the parameters of a function as a tuple.</p>
<pre><code class="lang-ts"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">greet</span>(<span class="hljs-params">name: <span class="hljs-built_in">string</span>, age: <span class="hljs-built_in">number</span></span>) </span>{}
<span class="hljs-keyword">type</span> GreetParams = Parameters&lt;<span class="hljs-keyword">typeof</span> greet&gt;; <span class="hljs-comment">// [string, number]</span>
</code></pre>
<h3 id="heading-12-constructorparameters-gets-constructor-parameters"><strong>12.</strong> <code>ConstructorParameters&lt;T&gt;</code> - Gets Constructor Parameters</h3>
<p>Extracts parameters from a class constructor.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">class</span> Person {
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">public</span> name: <span class="hljs-built_in">string</span>, <span class="hljs-keyword">public</span> age: <span class="hljs-built_in">number</span></span>) {}
}
<span class="hljs-keyword">type</span> PersonParams = ConstructorParameters&lt;<span class="hljs-keyword">typeof</span> Person&gt;; <span class="hljs-comment">// [string, number]</span>
</code></pre>
<h3 id="heading-13-instancetype-gets-the-instance-type-of-a-class"><strong>13.</strong> <code>InstanceType&lt;T&gt;</code> - Gets the Instance Type of a Class</h3>
<p>Extracts the type of an instance of a class.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">class</span> Car { name: <span class="hljs-built_in">string</span> = <span class="hljs-string">"Tesla"</span>; }
<span class="hljs-keyword">type</span> CarInstance = InstanceType&lt;<span class="hljs-keyword">typeof</span> Car&gt;; <span class="hljs-comment">// Car</span>
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>TypeScript <strong>Utility Types</strong> are incredibly useful for improving <strong>code reusability, reducing redundancy, and making type definitions more flexible</strong>. Whether you're simplifying object structures, extracting properties, or enforcing immutability, these utilities help you write <strong>better, safer, and more maintainable</strong> TypeScript code.</p>
<p>By mastering these utility types, you'll be able to write cleaner, more efficient code while minimizing errors. Next time you find yourself modifying types manually, check if there's a utility type that can <strong>do the work for you</strong>!</p>
]]></content:encoded></item><item><title><![CDATA[Understanding the JavaScript Event Loop and Microtasks: A Beginner’s Guide]]></title><description><![CDATA[Introduction
JavaScript is known for being a single-threaded language, which means it can only do one thing at a time. But if that’s the case, how does it handle things like fetching data, responding to user interactions, or running animations withou...]]></description><link>https://blog.iamdipankarpaul.com/understanding-the-javascript-event-loop-and-microtasks-a-beginners-guide</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/understanding-the-javascript-event-loop-and-microtasks-a-beginners-guide</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Event Loop]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[asynchronous JavaScript]]></category><category><![CDATA[async/await]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Mon, 31 Mar 2025 18:30:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1749276088858/5593b12d-3eb8-4283-8c3a-1303e1909432.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>JavaScript is known for being a single-threaded language, which means it can only do one thing at a time. But if that’s the case, how does it handle things like fetching data, responding to user interactions, or running animations without freezing the entire page? The answer lies in the <strong>Event Loop</strong> - a clever mechanism that makes JavaScript feel fast and responsive.</p>
<p>If you’ve ever wondered why <code>setTimeout()</code> <strong>doesn’t always run immediately</strong>, or why <strong>Promises resolve before</strong> <code>setTimeout()</code>, you’re about to get all the answers! In this guide, we’ll break down the JavaScript Event Loop and Microtasks in the simplest way possible.</p>
<h2 id="heading-what-is-the-javascript-event-loop">What is the JavaScript Event Loop?</h2>
<p>The <strong>Event Loop</strong> is like a manager that ensures JavaScript runs efficiently by deciding what to execute next. Since JavaScript can’t perform multiple operations at the same time, it needs a structured way to handle tasks.</p>
<p>Imagine a queue at a coffee shop:</p>
<ol>
<li><p>You place an order (a task gets added to the queue).</p>
</li>
<li><p>The barista prepares your coffee (the JavaScript engine processes your task).</p>
</li>
<li><p>When your coffee is ready, your name is called (your task is completed).</p>
</li>
</ol>
<p>Similarly, JavaScript organizes tasks and processes them in an order that ensures smooth performance.</p>
<h2 id="heading-how-javascript-handles-tasks">How JavaScript Handles Tasks</h2>
<p>In JavaScript, tasks are divided into <strong>synchronous</strong> and <strong>asynchronous</strong> operations:</p>
<h3 id="heading-1-synchronous-tasks-main-thread">1. <strong>Synchronous Tasks</strong> (Main Thread)</h3>
<p>These are regular JavaScript statements that execute <strong>one after another</strong>, blocking the execution of further code until they finish.</p>
<pre><code class="lang-js"><span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Task 1"</span>);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Task 2"</span>);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Task 3"</span>);
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-bash">Task 1
Task 2
Task 3
</code></pre>
<p>There’s nothing special here. JavaScript reads and executes these lines from top to bottom.</p>
<h3 id="heading-2-asynchronous-tasks-handled-via-event-loop">2. <strong>Asynchronous Tasks</strong> (Handled via Event Loop)</h3>
<p>These include operations that <strong>take time</strong> to complete, such as fetching data, waiting for a timer, or responding to user actions.</p>
<pre><code class="lang-js"><span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Start"</span>);

<span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Async Task"</span>), <span class="hljs-number">1000</span>);

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"End"</span>);
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-bash">Start
End
Async Task  (after 1 second)
</code></pre>
<p>Even though <code>setTimeout</code> was called first, JavaScript doesn’t wait for it - it moves on to the next statement while the timer runs in the background.</p>
<h2 id="heading-microtasks-vs-macrotasks-understanding-the-priority-system">Microtasks vs. Macrotasks: Understanding the Priority System</h2>
<p>Not all asynchronous tasks are treated the same. JavaScript has two types of task queues:</p>
<h3 id="heading-1-microtasks-higher-priority"><strong>1. Microtasks (Higher Priority)</strong></h3>
<p>Microtasks include:</p>
<ul>
<li><p>Promises (<code>.then</code>, <code>.catch</code>, <code>.finally</code>)</p>
</li>
<li><p><code>queueMicrotask()</code></p>
</li>
<li><p><code>MutationObserver</code></p>
</li>
</ul>
<p>They are executed <strong>right after the main script</strong> finishes and <strong>before</strong> any macrotasks.</p>
<h3 id="heading-2-macrotasks-lower-priority"><strong>2. Macrotasks (Lower Priority)</strong></h3>
<p>Macrotasks include:</p>
<ul>
<li><p><code>setTimeout</code>, <code>setInterval</code></p>
</li>
<li><p><code>setImmediate</code> (Node.js only)</p>
</li>
<li><p><code>requestAnimationFrame</code></p>
</li>
</ul>
<p>These are scheduled <strong>after microtasks are completed</strong>.</p>
<h3 id="heading-execution-order-example"><strong>Execution Order Example</strong></h3>
<p>Let’s test this in code:</p>
<pre><code class="lang-js"><span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Start"</span>);
<span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Macrotask - setTimeout"</span>), <span class="hljs-number">0</span>);
<span class="hljs-built_in">Promise</span>.resolve().then(<span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Microtask - Promise"</span>));
queueMicrotask(<span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Microtask - queueMicrotask"</span>));
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"End"</span>);
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-bash">Start
End
Microtask - Promise
Microtask - queueMicrotask
Macrotask - setTimeout
</code></pre>
<p>👉 Even though <code>setTimeout</code> has <code>0</code> delay, it still runs <strong>after</strong> microtasks!</p>
<h2 id="heading-the-event-loop-in-action-step-by-step-breakdown">The Event Loop in Action (Step-by-Step Breakdown)</h2>
<p>Let’s understand how the <strong>Event Loop</strong> works by breaking it into simple steps:</p>
<ol>
<li><p>JavaScript <strong>runs the main script</strong> (top-to-bottom execution).</p>
</li>
<li><p><strong>Microtasks are executed next</strong> (Promises, <code>queueMicrotask</code>).</p>
</li>
<li><p><strong>Macrotasks run last</strong> (<code>setTimeout</code>, <code>setInterval</code>, etc.).</p>
</li>
<li><p>The Event Loop <strong>repeats the process</strong>, constantly checking for new tasks.</p>
</li>
</ol>
<p>Here's a visual representation:</p>
<pre><code class="lang-bash">  ┌───────────────────────────────────────────┐
  │ JavaScript Code Execution (Main Thread)   │
  └───────────────────────────────────────────┘
             │
             ▼
  ┌───────────────────────────────────────────┐
  │ **Microtask Queue** (Higher Priority)     │
  │ Promises, queueMicrotask                  │
  └───────────────────────────────────────────┘
             │
             ▼
  ┌───────────────────────────────────────────┐
  │ **Macrotask Queue** (Lower Priority)      │
  │ setTimeout, setInterval, setImmediate     │
  └───────────────────────────────────────────┘
</code></pre>
<h2 id="heading-common-misconceptions-about-the-event-loop">Common Misconceptions about the Event Loop</h2>
<ol>
<li><p><strong>"</strong><code>setTimeout(0)</code> <strong>runs immediately"</strong> ❌</p>
<ul>
<li>No, it waits until the main script and all microtasks are done first.</li>
</ul>
</li>
<li><p><strong>"JavaScript runs everything at the same time"</strong> ❌</p>
<ul>
<li>No, it runs one task at a time, handling async tasks using the event loop.</li>
</ul>
</li>
<li><p><strong>"Microtasks and macrotasks are the same"</strong> ❌</p>
<ul>
<li>No, <strong>microtasks run first</strong>, making Promises faster than setTimeout.</li>
</ul>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The <strong>JavaScript Event Loop</strong> is what makes JavaScript <strong>non-blocking</strong> and efficient. It ensures that tasks are handled in the right order - <strong>synchronous tasks first, microtasks next, and macrotasks last</strong>. This allows JavaScript to handle user interactions, API requests, and timers without freezing the page.</p>
<p>Understanding this concept will help you debug performance issues, optimize asynchronous operations, and write better JavaScript code. So next time you see a <code>setTimeout</code>, <code>Promise</code>, or <code>async/await</code>, you'll know exactly how JavaScript is handling them! 🚀</p>
]]></content:encoded></item><item><title><![CDATA[SOLID Principles in React]]></title><description><![CDATA[The SOLID principles can be highly beneficial when developing React applications. Let's explore how each principle translates to React development with practical examples.
1. Single Responsibility Principle (SRP)
In React, this means each component s...]]></description><link>https://blog.iamdipankarpaul.com/solid-principles-in-react</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/solid-principles-in-react</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Sat, 15 Mar 2025 03:30:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1749276382634/fea84b20-c9cb-4117-94c5-e415fef5ac33.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The SOLID principles can be highly beneficial when developing React applications. Let's explore how each principle translates to React development with practical examples.</p>
<h2 id="heading-1-single-responsibility-principle-srp">1. Single Responsibility Principle (SRP)</h2>
<p>In React, this means each component should focus on doing one thing well. Components should have a single reason to change.</p>
<h3 id="heading-example-breaking-down-a-complex-form">Example: Breaking Down a Complex Form</h3>
<p><strong>Violating SRP:</strong></p>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserProfilePage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [user, setUser] = useState({});
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(<span class="hljs-literal">true</span>);
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>);

  <span class="hljs-comment">// Fetch user data</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    fetchUserData()
      .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
        setUser(data);
        setIsLoading(<span class="hljs-literal">false</span>);
      })
      .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
        setError(err);
        setIsLoading(<span class="hljs-literal">false</span>);
      });
  }, []);

  <span class="hljs-comment">// Form handling logic</span>
  <span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    event.preventDefault();
    <span class="hljs-comment">// Validation logic</span>
    <span class="hljs-comment">// API submission logic</span>
    <span class="hljs-comment">// Success/error handling</span>
  };

  <span class="hljs-keyword">if</span> (isLoading) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">LoadingSpinner</span> /&gt;</span></span>;
  <span class="hljs-keyword">if</span> (error) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ErrorMessage</span> <span class="hljs-attr">error</span>=<span class="hljs-string">{error}</span> /&gt;</span></span>;

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{user.name}'s Profile<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmit}</span>&gt;</span>
        {/* Many form fields */}
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{user.name}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{/*</span> <span class="hljs-attr">...</span> */} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{user.email}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{/*</span> <span class="hljs-attr">...</span> */} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{user.bio}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{/*</span> <span class="hljs-attr">...</span> */} /&gt;</span>
        {/* More fields, validation display, etc. */}
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Save Profile<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p><strong>Following SRP:</strong></p>
<pre><code class="lang-jsx"><span class="hljs-comment">// Data fetching component</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserProfileContainer</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [user, setUser] = useState({});
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(<span class="hljs-literal">true</span>);
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    fetchUserData()
      .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
        setUser(data);
        setIsLoading(<span class="hljs-literal">false</span>);
      })
      .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
        setError(err);
        setIsLoading(<span class="hljs-literal">false</span>);
      });
  }, []);

  <span class="hljs-keyword">if</span> (isLoading) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">LoadingSpinner</span> /&gt;</span></span>;
  <span class="hljs-keyword">if</span> (error) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ErrorMessage</span> <span class="hljs-attr">error</span>=<span class="hljs-string">{error}</span> /&gt;</span></span>;

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">UserProfileForm</span> <span class="hljs-attr">user</span>=<span class="hljs-string">{user}</span> /&gt;</span></span>;
}

<span class="hljs-comment">// Presentation and form handling component</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserProfileForm</span>(<span class="hljs-params">{ user }</span>) </span>{
  <span class="hljs-keyword">const</span> [formData, setFormData] = useState(user);

  <span class="hljs-keyword">const</span> handleChange = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    setFormData({...formData, [e.target.name]: e.target.value});
  };

  <span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    event.preventDefault();
    updateUserProfile(formData);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{formData.name}'s Profile<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmit}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ProfileField</span> 
          <span class="hljs-attr">label</span>=<span class="hljs-string">"Name"</span> 
          <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span> 
          <span class="hljs-attr">value</span>=<span class="hljs-string">{formData.name}</span> 
          <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleChange}</span> 
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ProfileField</span> 
          <span class="hljs-attr">label</span>=<span class="hljs-string">"Email"</span> 
          <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span> 
          <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> 
          <span class="hljs-attr">value</span>=<span class="hljs-string">{formData.email}</span> 
          <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleChange}</span> 
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">BioField</span> 
          <span class="hljs-attr">value</span>=<span class="hljs-string">{formData.bio}</span> 
          <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleChange}</span> 
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Save Profile<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// Reusable form field component</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ProfileField</span>(<span class="hljs-params">{ label, name, type = <span class="hljs-string">"text"</span>, value, onChange }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"form-field"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">{name}</span>&gt;</span>{label}<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> 
        <span class="hljs-attr">id</span>=<span class="hljs-string">{name}</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">{name}</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">{type}</span>
        <span class="hljs-attr">value</span>=<span class="hljs-string">{value}</span>
        <span class="hljs-attr">onChange</span>=<span class="hljs-string">{onChange}</span>
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>This breakdown creates:</p>
<ul>
<li><p>Container component handling data fetching</p>
</li>
<li><p>Form component handling form state and submission</p>
</li>
<li><p>Field component for reusable input rendering</p>
</li>
</ul>
<p>Each has a single responsibility and reason to change.</p>
<h2 id="heading-2-openclosed-principle-ocp">2. Open/Closed Principle (OCP)</h2>
<p>React components should be open for extension but closed for modification. This means you should be able to add new functionality without changing existing components.</p>
<h3 id="heading-example-extensible-button-component">Example: Extensible Button Component</h3>
<p><strong>Following OCP:</strong></p>
<pre><code class="lang-jsx"><span class="hljs-comment">// Base Button component that's closed for modification</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Button</span>(<span class="hljs-params">{ children, onClick, ...props }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
      <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onClick}</span> 
      <span class="hljs-attr">className</span>=<span class="hljs-string">"btn"</span> 
      {<span class="hljs-attr">...props</span>}
    &gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// Extended components without modifying the base Button</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">PrimaryButton</span>(<span class="hljs-params">props</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"btn btn-primary"</span> {<span class="hljs-attr">...props</span>} /&gt;</span></span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">DangerButton</span>(<span class="hljs-params">props</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"btn btn-danger"</span> {<span class="hljs-attr">...props</span>} /&gt;</span></span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">IconButton</span>(<span class="hljs-params">{ icon, ...props }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Button</span> {<span class="hljs-attr">...props</span>}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"icon"</span>&gt;</span>{icon}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      {props.children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">Button</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// Further extension via composition</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">SubmitButton</span>(<span class="hljs-params">props</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">PrimaryButton</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> {<span class="hljs-attr">...props</span>}&gt;</span>
      Submit
    <span class="hljs-tag">&lt;/<span class="hljs-name">PrimaryButton</span>&gt;</span></span>
  );
}
</code></pre>
<p>The base <code>Button</code> component is closed for modification but open for extension through composition, props spreading, and specialization.</p>
<h3 id="heading-example-render-props-pattern-for-ocp">Example: Render Props Pattern for OCP</h3>
<pre><code class="lang-jsx"><span class="hljs-comment">// A component that's closed for modification but extendable</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">DataFetcher</span>(<span class="hljs-params">{ url, render }</span>) </span>{
  <span class="hljs-keyword">const</span> [data, setData] = useState(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">true</span>);
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    setLoading(<span class="hljs-literal">true</span>);
    fetch(url)
      .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> response.json())
      .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
        setData(data);
        setLoading(<span class="hljs-literal">false</span>);
      })
      .catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
        setError(error);
        setLoading(<span class="hljs-literal">false</span>);
      });
  }, [url]);

  <span class="hljs-keyword">return</span> render({ data, loading, error });
}

<span class="hljs-comment">// Different ways to extend without modifying DataFetcher</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserList</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">DataFetcher</span> 
      <span class="hljs-attr">url</span>=<span class="hljs-string">"/api/users"</span> 
      <span class="hljs-attr">render</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">data</span>, <span class="hljs-attr">loading</span>, <span class="hljs-attr">error</span> }) =&gt;</span> {
        if (loading) return <span class="hljs-tag">&lt;<span class="hljs-name">LoadingSpinner</span> /&gt;</span>;
        if (error) return <span class="hljs-tag">&lt;<span class="hljs-name">ErrorMessage</span> <span class="hljs-attr">error</span>=<span class="hljs-string">{error}</span> /&gt;</span>;
        return <span class="hljs-tag">&lt;<span class="hljs-name">UserTable</span> <span class="hljs-attr">users</span>=<span class="hljs-string">{data}</span> /&gt;</span>;
      }} 
    /&gt;</span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ProductDetails</span>(<span class="hljs-params">{ productId }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">DataFetcher</span> 
      <span class="hljs-attr">url</span>=<span class="hljs-string">{</span>`/<span class="hljs-attr">api</span>/<span class="hljs-attr">products</span>/${<span class="hljs-attr">productId</span>}`} 
      <span class="hljs-attr">render</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">data</span>, <span class="hljs-attr">loading</span>, <span class="hljs-attr">error</span> }) =&gt;</span> {
        if (loading) return <span class="hljs-tag">&lt;<span class="hljs-name">ProductSkeleton</span> /&gt;</span>;
        if (error) return <span class="hljs-tag">&lt;<span class="hljs-name">ProductError</span> /&gt;</span>;
        return <span class="hljs-tag">&lt;<span class="hljs-name">ProductView</span> <span class="hljs-attr">product</span>=<span class="hljs-string">{data}</span> /&gt;</span>;
      }} 
    /&gt;</span>
  );
}
</code></pre>
<p>The <code>DataFetcher</code> component is closed for modification but can be extended for many different use cases through the render prop pattern.</p>
<h2 id="heading-3-liskov-substitution-principle-lsp">3. Liskov Substitution Principle (LSP)</h2>
<p>In React, this means child components should be substitutable for their parent components without affecting the correctness of the application.</p>
<h3 id="heading-example-component-inheritancecomposition">Example: Component Inheritance/Composition</h3>
<pre><code class="lang-jsx"><span class="hljs-comment">// Base Card component with a consistent interface</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Card</span>(<span class="hljs-params">{ title, children, footer }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"card"</span>&gt;</span>
      {title &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"card-header"</span>&gt;</span>{title}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>}
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"card-body"</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      {footer &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"card-footer"</span>&gt;</span>{footer}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// UserCard extends Card functionality while remaining substitutable</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserCard</span>(<span class="hljs-params">{ user, ...props }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Card</span> 
      <span class="hljs-attr">title</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">UserHeader</span> <span class="hljs-attr">user</span>=<span class="hljs-string">{user}</span> /&gt;</span>}
      footer={<span class="hljs-tag">&lt;<span class="hljs-name">UserFooter</span> <span class="hljs-attr">user</span>=<span class="hljs-string">{user}</span> /&gt;</span>}
      {...props}
    &gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">UserDetails</span> <span class="hljs-attr">user</span>=<span class="hljs-string">{user}</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Card</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// ProductCard also extends Card and remains substitutable</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ProductCard</span>(<span class="hljs-params">{ product, ...props }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Card</span> 
      <span class="hljs-attr">title</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">ProductHeader</span> <span class="hljs-attr">product</span>=<span class="hljs-string">{product}</span> /&gt;</span>}
      footer={<span class="hljs-tag">&lt;<span class="hljs-name">PriceFooter</span> <span class="hljs-attr">price</span>=<span class="hljs-string">{product.price}</span> /&gt;</span>}
      {...props}
    &gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">ProductDetails</span> <span class="hljs-attr">product</span>=<span class="hljs-string">{product}</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Card</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// This function can work with ANY type of Card component</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">CardGrid</span>(<span class="hljs-params">{ items, CardComponent }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"card-grid"</span>&gt;</span>
      {items.map(item =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">CardComponent</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{item.id}</span> {<span class="hljs-attr">...item</span>} /&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// Usage</span>
&lt;CardGrid items={users} CardComponent={UserCard} /&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">CardGrid</span> <span class="hljs-attr">items</span>=<span class="hljs-string">{products}</span> <span class="hljs-attr">CardComponent</span>=<span class="hljs-string">{ProductCard}</span> /&gt;</span></span>
</code></pre>
<p>Any type of <code>Card</code> component can be substituted in the <code>CardGrid</code> without breaking its functionality, adhering to LSP.</p>
<h2 id="heading-4-interface-segregation-principle-isp">4. Interface Segregation Principle (ISP)</h2>
<p>Components should not be forced to depend on props they don't use. It's better to have multiple smaller, focused components than one large component with many props.</p>
<h3 id="heading-example-breaking-down-a-complex-component">Example: Breaking Down a Complex Component</h3>
<p><strong>Violating ISP:</strong></p>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">DataTable</span>(<span class="hljs-params">{ 
  data, 
  columns, 
  sortable, 
  sortColumn, 
  sortDirection, 
  onSort,
  filterable,
  filters,
  onFilter,
  pageable,
  page,
  pageSize,
  totalPages,
  onPageChange,
  selectable,
  selectedItems,
  onSelect,
  editable,
  onEdit,
  deletable,
  onDelete,
  <span class="hljs-regexp">//</span> Many more props...
}</span>) </span>{
  <span class="hljs-comment">// Complex implementation handling all these features</span>
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      {/* Table implementation */}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p><strong>Following ISP:</strong></p>
<pre><code class="lang-jsx"><span class="hljs-comment">// Core table component with minimal interface</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">DataTable</span>(<span class="hljs-params">{ data, columns, children }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">table</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">thead</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
          {columns.map(column =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">th</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{column.key}</span>&gt;</span>{column.title}<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
          ))}
        <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">thead</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span>&gt;</span>
        {data.map(row =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">tr</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{row.id}</span>&gt;</span>
            {columns.map(column =&gt; (
              <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{column.key}</span>&gt;</span>{row[column.key]}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
            ))}
          <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
        ))}
      <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// Specialized components with focused interfaces</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">SortableHeader</span>(<span class="hljs-params">{ column, sortDirection, onSort }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">th</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> onSort(column.key)}&gt;
      {column.title} {sortDirection === 'asc' ? '↑' : '↓'}
    <span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TablePagination</span>(<span class="hljs-params">{ page, totalPages, onPageChange }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">tfoot</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">colSpan</span>=<span class="hljs-string">"100%"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"pagination"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> onPageChange(page - 1)} disabled={page === 1}&gt;
              Previous
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Page {page} of {totalPages}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> onPageChange(page + 1)} disabled={page === totalPages}&gt;
              Next
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">tfoot</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// Compose them as needed without forcing unused props</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ProductTable</span>(<span class="hljs-params">{ products }</span>) </span>{
  <span class="hljs-comment">// Only implement pagination, no sorting or filtering</span>
  <span class="hljs-keyword">const</span> [page, setPage] = useState(<span class="hljs-number">1</span>);
  <span class="hljs-keyword">const</span> pageSize = <span class="hljs-number">10</span>;
  <span class="hljs-keyword">const</span> totalPages = <span class="hljs-built_in">Math</span>.ceil(products.length / pageSize);
  <span class="hljs-keyword">const</span> displayProducts = products.slice((page - <span class="hljs-number">1</span>) * pageSize, page * pageSize);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">DataTable</span> 
      <span class="hljs-attr">data</span>=<span class="hljs-string">{displayProducts}</span> 
      <span class="hljs-attr">columns</span>=<span class="hljs-string">{productColumns}</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">TablePagination</span> 
        <span class="hljs-attr">page</span>=<span class="hljs-string">{page}</span> 
        <span class="hljs-attr">totalPages</span>=<span class="hljs-string">{totalPages}</span> 
        <span class="hljs-attr">onPageChange</span>=<span class="hljs-string">{setPage}</span> 
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">DataTable</span>&gt;</span></span>
  );
}
</code></pre>
<p>Each component has a focused interface with only the props it needs, following ISP.</p>
<h2 id="heading-5-dependency-inversion-principle-dip">5. Dependency Inversion Principle (DIP)</h2>
<p>High-level components should not depend on low-level components. Both should depend on abstractions.</p>
<h3 id="heading-example-isolating-api-dependencies">Example: Isolating API Dependencies</h3>
<p><strong>Violating DIP:</strong></p>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserList</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [users, setUsers] = useState([]);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// Direct dependency on API implementation</span>
    fetch(<span class="hljs-string">'/api/users'</span>)
      .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> response.json())
      .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> setUsers(data));
  }, []);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
      {users.map(user =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span></span>
  );
}
</code></pre>
<p><strong>Following DIP:</strong></p>
<pre><code class="lang-jsx"><span class="hljs-comment">// Abstract API service</span>
<span class="hljs-keyword">const</span> userService = {
  <span class="hljs-attr">getUsers</span>: <span class="hljs-function">() =&gt;</span> fetch(<span class="hljs-string">'/api/users'</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json()),
  <span class="hljs-attr">getUser</span>: <span class="hljs-function">(<span class="hljs-params">id</span>) =&gt;</span> fetch(<span class="hljs-string">`/api/users/<span class="hljs-subst">${id}</span>`</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json()),
  <span class="hljs-attr">updateUser</span>: <span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> fetch(<span class="hljs-string">`/api/users/<span class="hljs-subst">${user.id}</span>`</span>, {
    <span class="hljs-attr">method</span>: <span class="hljs-string">'PUT'</span>,
    <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(user),
    <span class="hljs-attr">headers</span>: { <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span> }
  }).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json())
};

<span class="hljs-comment">// Custom hook that depends on abstractions</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useUserData</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [users, setUsers] = useState([]);
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">true</span>);
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    setLoading(<span class="hljs-literal">true</span>);
    userService.getUsers()
      .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
        setUsers(data);
        setLoading(<span class="hljs-literal">false</span>);
      })
      .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
        setError(err);
        setLoading(<span class="hljs-literal">false</span>);
      });
  }, []);

  <span class="hljs-keyword">return</span> { users, loading, error };
}

<span class="hljs-comment">// Component depends on the abstraction, not the implementation</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserList</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { users, loading, error } = useUserData();

  <span class="hljs-keyword">if</span> (loading) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Loading...<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>;
  <span class="hljs-keyword">if</span> (error) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Error: {error.message}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>;

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
      {users.map(user =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span></span>
  );
}
</code></pre>
<h3 id="heading-example-dependency-injection-in-react-with-context">Example: Dependency Injection in React with Context</h3>
<pre><code class="lang-jsx"><span class="hljs-comment">// Create an abstract API context</span>
<span class="hljs-keyword">const</span> ApiContext = React.createContext(<span class="hljs-literal">null</span>);

<span class="hljs-comment">// Provider component that injects the dependency</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ApiProvider</span>(<span class="hljs-params">{ apiClient, children }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ApiContext.Provider</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{apiClient}</span>&gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ApiContext.Provider</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// Hook to consume the abstraction</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useApi</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> api = useContext(ApiContext);
  <span class="hljs-keyword">if</span> (!api) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'useApi must be used within an ApiProvider'</span>);
  }
  <span class="hljs-keyword">return</span> api;
}

<span class="hljs-comment">// Component that depends on the abstraction</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserList</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> api = useApi();
  <span class="hljs-keyword">const</span> [users, setUsers] = useState([]);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    api.getUsers().then(setUsers);
  }, [api]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
      {users.map(user =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span></span>
  );
}

<span class="hljs-comment">// App setup with dependency injection</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// We can inject different implementations</span>
  <span class="hljs-keyword">const</span> productionApi = {
    <span class="hljs-attr">getUsers</span>: <span class="hljs-function">() =&gt;</span> fetch(<span class="hljs-string">'/api/users'</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json())
  };

  <span class="hljs-keyword">const</span> mockApi = {
    <span class="hljs-attr">getUsers</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">Promise</span>.resolve([
      { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'Test User'</span> }
    ])
  };

  <span class="hljs-comment">// Choose which implementation to inject</span>
  <span class="hljs-keyword">const</span> api = process.env.NODE_ENV === <span class="hljs-string">'test'</span> ? mockApi : productionApi;

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ApiProvider</span> <span class="hljs-attr">apiClient</span>=<span class="hljs-string">{api}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">UserList</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ApiProvider</span>&gt;</span></span>
  );
}
</code></pre>
<p>This approach follows DIP by:</p>
<ol>
<li><p>Creating an abstraction (the API context)</p>
</li>
<li><p>High-level components (UserList) depend on the abstraction, not concrete implementations</p>
</li>
<li><p>We can easily inject different implementations without changing the components</p>
</li>
</ol>
<h2 id="heading-practical-applications-of-solid-in-react">Practical Applications of SOLID in React</h2>
<h3 id="heading-custom-hooks-for-srp-and-dip">Custom Hooks for SRP and DIP</h3>
<pre><code class="lang-jsx"><span class="hljs-comment">// Separate concerns with custom hooks</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useUserApi</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> api = useApi();
  <span class="hljs-keyword">const</span> [users, setUsers] = useState([]);
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>);

  <span class="hljs-keyword">const</span> fetchUsers = useCallback(<span class="hljs-function">() =&gt;</span> {
    setLoading(<span class="hljs-literal">true</span>);
    api.getUsers()
      .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
        setUsers(data);
        setLoading(<span class="hljs-literal">false</span>);
      })
      .catch(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
        setError(err);
        setLoading(<span class="hljs-literal">false</span>);
      });
  }, [api]);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    fetchUsers();
  }, [fetchUsers]);

  <span class="hljs-keyword">return</span> { users, loading, error, <span class="hljs-attr">refetch</span>: fetchUsers };
}

<span class="hljs-comment">// Component just handles presentation</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserList</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { users, loading, error, refetch } = useUserApi();

  <span class="hljs-keyword">if</span> (loading) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">LoadingSpinner</span> /&gt;</span></span>;
  <span class="hljs-keyword">if</span> (error) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ErrorMessage</span> <span class="hljs-attr">error</span>=<span class="hljs-string">{error}</span> <span class="hljs-attr">onRetry</span>=<span class="hljs-string">{refetch}</span> /&gt;</span></span>;

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Users<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        {users.map(user =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        ))}
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<h3 id="heading-higher-order-components-for-ocp">Higher-Order Components for OCP</h3>
<pre><code class="lang-jsx"><span class="hljs-comment">// HOC that adds loading state to any component</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withLoading</span>(<span class="hljs-params">WrappedComponent</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">WithLoadingComponent</span>(<span class="hljs-params">{ isLoading, ...props }</span>) </span>{
    <span class="hljs-keyword">if</span> (isLoading) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">LoadingSpinner</span> /&gt;</span></span>;
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">WrappedComponent</span> {<span class="hljs-attr">...props</span>} /&gt;</span></span>;
  };
}

<span class="hljs-comment">// Usage</span>
<span class="hljs-keyword">const</span> UserListWithLoading = withLoading(UserList);
</code></pre>
<h3 id="heading-compound-components-for-isp">Compound Components for ISP</h3>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Tabs</span>(<span class="hljs-params">{ children, defaultTab }</span>) </span>{
  <span class="hljs-keyword">const</span> [activeTab, setActiveTab] = useState(defaultTab);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TabsContext.Provider</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">activeTab</span>, <span class="hljs-attr">setActiveTab</span> }}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tabs-container"</span>&gt;</span>
        {children}
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">TabsContext.Provider</span>&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TabList</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tab-list"</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Tab</span>(<span class="hljs-params">{ id, children }</span>) </span>{
  <span class="hljs-keyword">const</span> { activeTab, setActiveTab } = useContext(TabsContext);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
      <span class="hljs-attr">className</span>=<span class="hljs-string">{activeTab</span> === <span class="hljs-string">id</span> ? '<span class="hljs-attr">active</span>' <span class="hljs-attr">:</span> ''} 
      <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setActiveTab(id)}
    &gt;
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TabPanels</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tab-panels"</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">TabPanel</span>(<span class="hljs-params">{ id, children }</span>) </span>{
  <span class="hljs-keyword">const</span> { activeTab } = useContext(TabsContext);

  <span class="hljs-keyword">if</span> (activeTab !== id) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tab-panel"</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
}

<span class="hljs-comment">// Assign as compound components</span>
Tabs.TabList = TabList;
Tabs.Tab = Tab;
Tabs.TabPanels = TabPanels;
Tabs.TabPanel = TabPanel;

<span class="hljs-comment">// Usage</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Tabs</span> <span class="hljs-attr">defaultTab</span>=<span class="hljs-string">"users"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Tabs.TabList</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Tabs.Tab</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"users"</span>&gt;</span>Users<span class="hljs-tag">&lt;/<span class="hljs-name">Tabs.Tab</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Tabs.Tab</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"products"</span>&gt;</span>Products<span class="hljs-tag">&lt;/<span class="hljs-name">Tabs.Tab</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Tabs.TabList</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">Tabs.TabPanels</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Tabs.TabPanel</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"users"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">UserList</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Tabs.TabPanel</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Tabs.TabPanel</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"products"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ProductList</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Tabs.TabPanel</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Tabs.TabPanels</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Tabs</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-benefits-of-solid-in-react">Benefits of SOLID in React</h2>
<p>Applying SOLID principles to React applications yields several benefits:</p>
<ol>
<li><p><strong>Maintainability</strong>: Components with single responsibilities are easier to understand and modify.</p>
</li>
<li><p><strong>Reusability</strong>: Well-designed components with clear interfaces can be reused across projects.</p>
</li>
<li><p><strong>Testability</strong>: Components that follow SOLID are generally easier to test in isolation.</p>
</li>
<li><p><strong>Scalability</strong>: Applications built with SOLID principles can grow without becoming unwieldy.</p>
</li>
<li><p><strong>Adaptability</strong>: When requirements change, SOLID components are easier to adapt.</p>
</li>
</ol>
<h2 id="heading-challenges-and-considerations">Challenges and Considerations</h2>
<ol>
<li><p><strong>Over-engineering</strong>: Be careful not to break down components too much, which can lead to "prop drilling" or unnecessary complexity.</p>
</li>
<li><p><strong>Performance</strong>: Too many small components might impact performance due to the overhead of component mounting/unmounting.</p>
</li>
<li><p><strong>Learning curve</strong>: SOLID principles require discipline and understanding, which might slow down initial development.</p>
</li>
<li><p><strong>Team alignment</strong>: The whole team needs to understand and follow these principles for consistency.</p>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The SOLID principles provide a robust framework for designing React applications. By following these principles, you can create more maintainable, scalable, and testable React code. Each principle addresses different aspects of component design:</p>
<ul>
<li><p>Single Responsibility: Each component should do one thing well</p>
</li>
<li><p>Open/Closed: Extend components through composition without modifying them</p>
</li>
<li><p>Liskov Substitution: Component variants should be interchangeable</p>
</li>
<li><p>Interface Segregation: Components should have focused, minimal props</p>
</li>
<li><p>Dependency Inversion: Components should depend on abstractions</p>
</li>
</ul>
<p>When these principles are applied thoughtfully, they can significantly improve the quality and longevity of your React applications.</p>
]]></content:encoded></item><item><title><![CDATA[SOLID Principles with JavaScript and TypeScript Functions]]></title><description><![CDATA[The SOLID principles are five fundamental design principles in object-oriented programming that help create more maintainable, flexible, and scalable code. Let's explore each principle with examples in both JavaScript and TypeScript.
1. Single Respon...]]></description><link>https://blog.iamdipankarpaul.com/solid-principles-with-javascript-and-typescript-functions</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/solid-principles-with-javascript-and-typescript-functions</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[SOLID principles]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Sat, 01 Mar 2025 06:01:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/g5jpH62pwes/upload/31e7aeaa1f81276dbb674523bf652f78.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The <strong>SOLID</strong> principles are five fundamental design principles in object-oriented programming that help create more maintainable, flexible, and scalable code. Let's explore each principle with examples in both JavaScript and TypeScript.</p>
<h2 id="heading-1-single-responsibility-principle-srp">1. Single Responsibility Principle (SRP)</h2>
<p>A function should do one thing and do it well.</p>
<h3 id="heading-javascript-example">JavaScript Example:</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Violating SRP - this function does too many things</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">processUser</span>(<span class="hljs-params">userData</span>) </span>{
  <span class="hljs-comment">// Validate user data</span>
  <span class="hljs-keyword">if</span> (!userData.name || !userData.email) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Invalid user data'</span>);
  }

  <span class="hljs-comment">// Save to database</span>
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Saving user <span class="hljs-subst">${userData.name}</span> to database`</span>);

  <span class="hljs-comment">// Send welcome email</span>
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Sending welcome email to <span class="hljs-subst">${userData.email}</span>`</span>);

  <span class="hljs-keyword">return</span> { <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span> };
}

<span class="hljs-comment">// Following SRP - each function has one responsibility</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validateUser</span>(<span class="hljs-params">userData</span>) </span>{
  <span class="hljs-keyword">if</span> (!userData.name || !userData.email) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Invalid user data'</span>);
  }
  <span class="hljs-keyword">return</span> userData;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">saveUser</span>(<span class="hljs-params">userData</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Saving user <span class="hljs-subst">${userData.name}</span> to database`</span>);
  <span class="hljs-keyword">return</span> userData;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">notifyUser</span>(<span class="hljs-params">userData</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Sending welcome email to <span class="hljs-subst">${userData.email}</span>`</span>);
  <span class="hljs-keyword">return</span> userData;
}

<span class="hljs-comment">// We can compose these functions when needed</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">processUser</span>(<span class="hljs-params">userData</span>) </span>{
  <span class="hljs-keyword">return</span> notifyUser(saveUser(validateUser(userData)));
}
</code></pre>
<h3 id="heading-typescript-example">TypeScript Example:</h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> UserData {
  name: <span class="hljs-built_in">string</span>;
  email: <span class="hljs-built_in">string</span>;
}

<span class="hljs-comment">// Each function has a clear, single purpose</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validateUser</span>(<span class="hljs-params">userData: UserData</span>): <span class="hljs-title">UserData</span> </span>{
  <span class="hljs-keyword">if</span> (!userData.name || !userData.email) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Invalid user data'</span>);
  }
  <span class="hljs-keyword">return</span> userData;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">saveUser</span>(<span class="hljs-params">userData: UserData</span>): <span class="hljs-title">UserData</span> </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Saving user <span class="hljs-subst">${userData.name}</span> to database`</span>);
  <span class="hljs-keyword">return</span> userData;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">notifyUser</span>(<span class="hljs-params">userData: UserData</span>): <span class="hljs-title">UserData</span> </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Sending welcome email to <span class="hljs-subst">${userData.email}</span>`</span>);
  <span class="hljs-keyword">return</span> userData;
}

<span class="hljs-comment">// These can be combined using function composition</span>
<span class="hljs-keyword">const</span> processUser = (userData: UserData): <span class="hljs-function"><span class="hljs-params">UserData</span> =&gt;</span> 
  notifyUser(saveUser(validateUser(userData)));
</code></pre>
<p>The SRP for functions means each function should have exactly one reason to change. In the improved example, if we need to modify how validation works, we only need to change the <code>validateUser</code> function without touching the other functions.</p>
<h2 id="heading-2-openclosed-principle-ocp">2. Open/Closed Principle (OCP)</h2>
<p>Functions should be open for extension but closed for modification.</p>
<h3 id="heading-javascript-example-1">JavaScript Example:</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Violating OCP - we'd need to modify this function to add new shapes</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateArea</span>(<span class="hljs-params">shape</span>) </span>{
  <span class="hljs-keyword">if</span> (shape.type === <span class="hljs-string">'rectangle'</span>) {
    <span class="hljs-keyword">return</span> shape.width * shape.height;
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (shape.type === <span class="hljs-string">'circle'</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.PI * shape.radius * shape.radius;
  }
  <span class="hljs-comment">// Need to modify this function for each new shape</span>
}

<span class="hljs-comment">// Following OCP - using a strategy pattern with functions</span>
<span class="hljs-keyword">const</span> areaStrategies = {
  <span class="hljs-attr">rectangle</span>: <span class="hljs-function">(<span class="hljs-params">shape</span>) =&gt;</span> shape.width * shape.height,
  <span class="hljs-attr">circle</span>: <span class="hljs-function">(<span class="hljs-params">shape</span>) =&gt;</span> <span class="hljs-built_in">Math</span>.PI * shape.radius * shape.radius,
  <span class="hljs-comment">// We can add new shapes by adding new functions here</span>
  <span class="hljs-attr">triangle</span>: <span class="hljs-function">(<span class="hljs-params">shape</span>) =&gt;</span> (shape.base * shape.height) / <span class="hljs-number">2</span>
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateArea</span>(<span class="hljs-params">shape</span>) </span>{
  <span class="hljs-keyword">if</span> (!areaStrategies[shape.type]) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Shape type <span class="hljs-subst">${shape.type}</span> not supported`</span>);
  }
  <span class="hljs-keyword">return</span> areaStrategies[shape.type](shape);
}

<span class="hljs-comment">// We can extend by adding new strategies without modifying calculateArea</span>
areaStrategies[<span class="hljs-string">'trapezoid'</span>] = <span class="hljs-function">(<span class="hljs-params">shape</span>) =&gt;</span> 
  (shape.base1 + shape.base2) * shape.height / <span class="hljs-number">2</span>;
</code></pre>
<p>The OCP for functions means we can add new functionality without modifying existing functions. In the examples above, we can add support for new shapes without changing the <code>calculateArea</code> function.</p>
<h2 id="heading-3-liskov-substitution-principle-lsp">3. Liskov Substitution Principle (LSP)</h2>
<p>For functions, this means that if a function accepts a certain type of argument, any subtype of that argument should work correctly as well.</p>
<h3 id="heading-javascript-example-2">JavaScript Example:</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Parent type function</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">printDetails</span>(<span class="hljs-params">animal</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-string">`This animal makes a sound: <span class="hljs-subst">${animal.makeSound()}</span>`</span>;
}

<span class="hljs-comment">// These adhere to the LSP</span>
<span class="hljs-keyword">const</span> dog = {
  <span class="hljs-attr">makeSound</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">"Woof"</span>
};

<span class="hljs-keyword">const</span> cat = {
  <span class="hljs-attr">makeSound</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">"Meow"</span>
};

<span class="hljs-comment">// LSP violation - this breaks the contract</span>
<span class="hljs-keyword">const</span> fish = {
  <span class="hljs-attr">swim</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">"Swimming..."</span>
  <span class="hljs-comment">// Missing makeSound method</span>
};

<span class="hljs-comment">// Following LSP - ensuring consistent behavior</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">makeSound</span>(<span class="hljs-params">animal</span>) </span>{
  <span class="hljs-comment">// Check if the method exists</span>
  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> animal.makeSound !== <span class="hljs-string">'function'</span>) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">'Animal must have makeSound method'</span>);
  }
  <span class="hljs-keyword">return</span> animal.makeSound();
}
</code></pre>
<h3 id="heading-typescript-example-1">TypeScript Example:</h3>
<pre><code class="lang-typescript"><span class="hljs-comment">// Using interfaces to enforce the contract</span>
<span class="hljs-keyword">interface</span> Soundable {
  makeSound(): <span class="hljs-built_in">string</span>;
}

<span class="hljs-comment">// Function expecting a Soundable</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAnimalSound</span>(<span class="hljs-params">animal: Soundable</span>): <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">return</span> animal.makeSound();
}

<span class="hljs-comment">// These implementations satisfy the interface</span>
<span class="hljs-keyword">const</span> dog: Soundable = {
  makeSound: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">"Woof"</span>
};

<span class="hljs-keyword">const</span> cat: Soundable = {
  makeSound: <span class="hljs-function">() =&gt;</span> <span class="hljs-string">"Meow"</span>
};

<span class="hljs-comment">// Using function types as substitutes</span>
<span class="hljs-keyword">type</span> SoundFunction = <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">string</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">makeSound</span>(<span class="hljs-params">soundFn: SoundFunction</span>): <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">return</span> soundFn();
}

<span class="hljs-comment">// These functions are substitutable</span>
<span class="hljs-keyword">const</span> barkSound: SoundFunction = <span class="hljs-function">() =&gt;</span> <span class="hljs-string">"Woof"</span>;
<span class="hljs-keyword">const</span> meowSound: SoundFunction = <span class="hljs-function">() =&gt;</span> <span class="hljs-string">"Meow"</span>;

<span class="hljs-built_in">console</span>.log(makeSound(barkSound)); <span class="hljs-comment">// "Woof"</span>
<span class="hljs-built_in">console</span>.log(makeSound(meowSound)); <span class="hljs-comment">// "Meow"</span>
</code></pre>
<p>The LSP for functions means any function that fulfills the expected contract should be able to replace another function with the same contract without breaking the program.</p>
<h2 id="heading-4-interface-segregation-principle-isp">4. Interface Segregation Principle (ISP)</h2>
<p>Function parameters should be minimal, and callers should not be forced to provide parameters they don't care about.</p>
<h3 id="heading-javascript-example-3">JavaScript Example:</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Violating ISP - too many parameters that might not be used</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createUser</span>(<span class="hljs-params">name, email, address, phoneNumber, birthDate, subscription</span>) </span>{
  <span class="hljs-comment">// Logic to create user</span>
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Creating user <span class="hljs-subst">${name}</span> with email <span class="hljs-subst">${email}</span>`</span>);
  <span class="hljs-keyword">if</span> (address) <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Address: <span class="hljs-subst">${address}</span>`</span>);
  <span class="hljs-keyword">if</span> (phoneNumber) <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Phone: <span class="hljs-subst">${phoneNumber}</span>`</span>);
  <span class="hljs-comment">// and so on...</span>
}

<span class="hljs-comment">// Following ISP - using object parameters with defaults</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createUser</span>(<span class="hljs-params">{ name, email, ...options } = {}</span>) </span>{
  <span class="hljs-keyword">if</span> (!name || !email) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Name and email are required'</span>);

  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Creating user <span class="hljs-subst">${name}</span> with email <span class="hljs-subst">${email}</span>`</span>);

  <span class="hljs-keyword">if</span> (options.address) <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Address: <span class="hljs-subst">${options.address}</span>`</span>);
  <span class="hljs-keyword">if</span> (options.phoneNumber) <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Phone: <span class="hljs-subst">${options.phoneNumber}</span>`</span>);
  <span class="hljs-comment">// Optional parameters don't clutter the interface</span>
}

<span class="hljs-comment">// Or breaking into more focused functions</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createBasicUser</span>(<span class="hljs-params">name, email</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Creating user <span class="hljs-subst">${name}</span> with email <span class="hljs-subst">${email}</span>`</span>);
  <span class="hljs-keyword">return</span> { name, email };
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addUserAddress</span>(<span class="hljs-params">user, address</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Adding address to user <span class="hljs-subst">${user.name}</span>`</span>);
  <span class="hljs-keyword">return</span> { ...user, address };
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addUserPhone</span>(<span class="hljs-params">user, phoneNumber</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Adding phone number to user <span class="hljs-subst">${user.name}</span>`</span>);
  <span class="hljs-keyword">return</span> { ...user, phoneNumber };
}
</code></pre>
<h3 id="heading-typescript-example-2">TypeScript Example:</h3>
<pre><code class="lang-typescript"><span class="hljs-comment">// Using optional parameters and interfaces</span>
<span class="hljs-keyword">interface</span> UserRequired {
  name: <span class="hljs-built_in">string</span>;
  email: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">interface</span> UserOptional {
  address?: <span class="hljs-built_in">string</span>;
  phone?: <span class="hljs-built_in">string</span>;
  birthDate?: <span class="hljs-built_in">Date</span>;
  preferences?: Record&lt;<span class="hljs-built_in">string</span>, <span class="hljs-built_in">any</span>&gt;;
}

<span class="hljs-keyword">type</span> User = UserRequired &amp; UserOptional;

<span class="hljs-comment">// Function only requires what it needs</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createUser</span>(<span class="hljs-params">required: UserRequired, optional: Partial&lt;UserOptional&gt; = {}</span>): <span class="hljs-title">User</span> </span>{
  <span class="hljs-keyword">return</span> { ...required, ...optional };
}

<span class="hljs-comment">// Functions focused on specific operations</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validateEmail</span>(<span class="hljs-params">email: <span class="hljs-built_in">string</span></span>): <span class="hljs-title">boolean</span> </span>{
  <span class="hljs-keyword">return</span> email.includes(<span class="hljs-string">'@'</span>);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">formatUserAddress</span>(<span class="hljs-params">address: <span class="hljs-built_in">string</span></span>): <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">return</span> address.toUpperCase();
}

<span class="hljs-comment">// Usage examples</span>
<span class="hljs-keyword">const</span> user = createUser({ 
  name: <span class="hljs-string">'John'</span>, 
  email: <span class="hljs-string">'john@example.com'</span> 
});

<span class="hljs-keyword">const</span> userWithDetails = createUser(
  { name: <span class="hljs-string">'Alice'</span>, email: <span class="hljs-string">'alice@example.com'</span> },
  { address: <span class="hljs-string">'123 Main St'</span>, phone: <span class="hljs-string">'555-1234'</span> }
);
</code></pre>
<p>The ISP for functions means we should create smaller, more focused functions that only require the parameters they actually need, making them easier to use and test.</p>
<h2 id="heading-5-dependency-inversion-principle-dip">5. Dependency Inversion Principle (DIP)</h2>
<p>High-level functions should not depend on low-level functions. Both should depend on abstractions.</p>
<h3 id="heading-javascript-example-4">JavaScript Example:</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// Violating DIP - direct dependency on low-level function</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">saveUserToMySQL</span>(<span class="hljs-params">user</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Saving <span class="hljs-subst">${user.name}</span> to MySQL database`</span>);
  <span class="hljs-comment">// MySQL-specific code here</span>
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createUser</span>(<span class="hljs-params">userData</span>) </span>{
  <span class="hljs-keyword">const</span> user = { <span class="hljs-attr">name</span>: userData.name, <span class="hljs-attr">email</span>: userData.email };
  saveUserToMySQL(user); <span class="hljs-comment">// Direct dependency on MySQL implementation</span>
  <span class="hljs-keyword">return</span> user;
}

<span class="hljs-comment">// Following DIP - passing the dependency as a function parameter</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createUser</span>(<span class="hljs-params">userData, saveFunction</span>) </span>{
  <span class="hljs-keyword">const</span> user = { <span class="hljs-attr">name</span>: userData.name, <span class="hljs-attr">email</span>: userData.email };
  saveFunction(user); <span class="hljs-comment">// Depends on abstraction, not implementation</span>
  <span class="hljs-keyword">return</span> user;
}

<span class="hljs-comment">// Different implementations that can be injected</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">saveUserToMySQL</span>(<span class="hljs-params">user</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Saving <span class="hljs-subst">${user.name}</span> to MySQL database`</span>);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">saveUserToMongoDB</span>(<span class="hljs-params">user</span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Saving <span class="hljs-subst">${user.name}</span> to MongoDB`</span>);
}

<span class="hljs-comment">// Usage</span>
createUser({ <span class="hljs-attr">name</span>: <span class="hljs-string">'John'</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">'john@example.com'</span> }, saveUserToMySQL);
createUser({ <span class="hljs-attr">name</span>: <span class="hljs-string">'Jane'</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">'jane@example.com'</span> }, saveUserToMongoDB);
</code></pre>
<h3 id="heading-typescript-example-3">TypeScript Example:</h3>
<pre><code class="lang-typescript"><span class="hljs-comment">// Using function types as abstractions</span>
<span class="hljs-keyword">interface</span> User {
  name: <span class="hljs-built_in">string</span>;
  email: <span class="hljs-built_in">string</span>;
}

<span class="hljs-comment">// Function type definition (the abstraction)</span>
<span class="hljs-keyword">type</span> SaveUserFn = <span class="hljs-function">(<span class="hljs-params">user: User</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;User&gt;;

<span class="hljs-comment">// High-level function depends on abstraction</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createUser</span>(<span class="hljs-params">
  userData: Partial&lt;User&gt;, 
  saveUser: SaveUserFn
</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">User</span>&gt; </span>{
  <span class="hljs-keyword">if</span> (!userData.name || !userData.email) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Name and email are required'</span>);
  }

  <span class="hljs-keyword">const</span> user: User = { 
    name: userData.name, 
    email: userData.email 
  };

  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> saveUser(user);
}

<span class="hljs-comment">// Low-level implementations</span>
<span class="hljs-keyword">const</span> saveToMySQL: SaveUserFn = <span class="hljs-keyword">async</span> (user: User): <span class="hljs-built_in">Promise</span>&lt;User&gt; =&gt; {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Saving <span class="hljs-subst">${user.name}</span> to MySQL database`</span>);
  <span class="hljs-comment">// MySQL-specific code would go here</span>
  <span class="hljs-keyword">return</span> user;
};

<span class="hljs-keyword">const</span> saveToMongoDB: SaveUserFn = <span class="hljs-keyword">async</span> (user: User): <span class="hljs-built_in">Promise</span>&lt;User&gt; =&gt; {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Saving <span class="hljs-subst">${user.name}</span> to MongoDB`</span>);
  <span class="hljs-comment">// MongoDB-specific code would go here</span>
  <span class="hljs-keyword">return</span> user;
};

<span class="hljs-comment">// We can easily inject different dependencies</span>
createUser({ name: <span class="hljs-string">'John'</span>, email: <span class="hljs-string">'john@example.com'</span> }, saveToMySQL);
createUser({ name: <span class="hljs-string">'Jane'</span>, email: <span class="hljs-string">'jane@example.com'</span> }, saveToMongoDB);
</code></pre>
<p>The DIP for functions means high-level functions should take adapter functions as parameters rather than importing and using low-level functions directly. This dependency injection allows for more flexible, testable code.</p>
<h2 id="heading-practical-applications-in-functional-javascripttypescript">Practical Applications in Functional JavaScript/TypeScript</h2>
<p>These principles are particularly relevant in functional programming approaches:</p>
<ol>
<li><p><strong>Single Responsibility</strong>: Pure functions that do one thing well</p>
</li>
<li><p><strong>Open/Closed</strong>: Function composition and higher-order functions to extend behavior</p>
</li>
<li><p><strong>Liskov Substitution</strong>: Function types and consistent interfaces for callbacks</p>
</li>
<li><p><strong>Interface Segregation</strong>: Partial application and currying to create more focused function interfaces</p>
</li>
<li><p><strong>Dependency Inversion</strong>: Dependency injection through function parameters</p>
</li>
</ol>
<h3 id="heading-example-of-all-principles-together-in-functional-style">Example of All Principles Together in Functional Style:</h3>
<pre><code class="lang-typescript"><span class="hljs-comment">// User types</span>
<span class="hljs-keyword">interface</span> UserData {
  name: <span class="hljs-built_in">string</span>;
  email: <span class="hljs-built_in">string</span>;
  address?: <span class="hljs-built_in">string</span>;
}

<span class="hljs-comment">// Database abstraction (for DIP)</span>
<span class="hljs-keyword">type</span> DatabaseFn = <span class="hljs-function">(<span class="hljs-params">data: <span class="hljs-built_in">any</span></span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">any</span>&gt;;

<span class="hljs-comment">// Notification abstraction (for DIP)</span>
<span class="hljs-keyword">type</span> NotifyFn = <span class="hljs-function">(<span class="hljs-params">user: UserData</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt;;

<span class="hljs-comment">// Single Responsibility functions</span>
<span class="hljs-keyword">const</span> validateUserData = (data: Partial&lt;UserData&gt;): <span class="hljs-function"><span class="hljs-params">UserData</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (!data.name || !data.name.trim()) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Name is required'</span>);
  }
  <span class="hljs-keyword">if</span> (!data.email || !data.email.includes(<span class="hljs-string">'@'</span>)) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Valid email is required'</span>);
  }

  <span class="hljs-keyword">return</span> data <span class="hljs-keyword">as</span> UserData;
};

<span class="hljs-comment">// Open for extension with different database implementations</span>
<span class="hljs-keyword">const</span> createSaveUserFn = <span class="hljs-function">(<span class="hljs-params">db: DatabaseFn</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">async</span> (userData: UserData): <span class="hljs-built_in">Promise</span>&lt;UserData&gt; =&gt; {
    <span class="hljs-keyword">await</span> db(userData);
    <span class="hljs-keyword">return</span> userData;
  };
};

<span class="hljs-comment">// Open for extension with different notification strategies</span>
<span class="hljs-keyword">const</span> createNotifyUserFn = <span class="hljs-function">(<span class="hljs-params">notifier: NotifyFn</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">async</span> (userData: UserData): <span class="hljs-built_in">Promise</span>&lt;UserData&gt; =&gt; {
    <span class="hljs-keyword">await</span> notifier(userData);
    <span class="hljs-keyword">return</span> userData;
  };
};

<span class="hljs-comment">// High-level function that's closed for modification but depends on abstractions</span>
<span class="hljs-keyword">const</span> processUser = <span class="hljs-keyword">async</span> (
  userData: Partial&lt;UserData&gt;,
  saveUser: <span class="hljs-function">(<span class="hljs-params">user: UserData</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;UserData&gt;,
  notifyUser: <span class="hljs-function">(<span class="hljs-params">user: UserData</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;UserData&gt;
): <span class="hljs-built_in">Promise</span>&lt;UserData&gt; =&gt; {
  <span class="hljs-comment">// Pipeline of operations</span>
  <span class="hljs-keyword">const</span> validatedUser = validateUserData(userData);
  <span class="hljs-keyword">const</span> savedUser = <span class="hljs-keyword">await</span> saveUser(validatedUser);
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> notifyUser(savedUser);
};

<span class="hljs-comment">// Concrete implementations</span>
<span class="hljs-keyword">const</span> saveToDatabase: DatabaseFn = <span class="hljs-keyword">async</span> (data) =&gt; {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Saving to database: <span class="hljs-subst">${<span class="hljs-built_in">JSON</span>.stringify(data)}</span>`</span>);
  <span class="hljs-keyword">return</span> data;
};

<span class="hljs-keyword">const</span> sendWelcomeEmail: NotifyFn = <span class="hljs-keyword">async</span> (user) =&gt; {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Sending welcome email to <span class="hljs-subst">${user.email}</span>`</span>);
};

<span class="hljs-comment">// Compose the full functionality</span>
<span class="hljs-keyword">const</span> saveUser = createSaveUserFn(saveToDatabase);
<span class="hljs-keyword">const</span> notifyUser = createNotifyUserFn(sendWelcomeEmail);

<span class="hljs-comment">// Usage</span>
processUser(
  { name: <span class="hljs-string">'John Doe'</span>, email: <span class="hljs-string">'john@example.com'</span> }, 
  saveUser, 
  notifyUser
).then(<span class="hljs-function"><span class="hljs-params">user</span> =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`User processing complete: <span class="hljs-subst">${user.name}</span>`</span>);
});
</code></pre>
<p>This functional approach leverages all SOLID principles:</p>
<ul>
<li><p>Each function has a single responsibility</p>
</li>
<li><p>We extend functionality by creating new functions, not modifying existing ones</p>
</li>
<li><p>Functions with the same signature are substitutable</p>
</li>
<li><p>Function parameters are minimal and focused</p>
</li>
<li><p>High-level functions depend on abstractions passed as parameters</p>
</li>
</ul>
<p>By applying SOLID principles to functions, you can create more maintainable, flexible, and testable code in JavaScript and TypeScript, particularly in functional programming paradigms or in modern frameworks that emphasize component composition.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding HashSet in Rust]]></title><description><![CDATA[In Rust, a HashSet is a collection that stores unique elements in no particular order, using a hash-based implementation for efficient membership tests. Essentially, a HashSet is similar to a HashMap where the value is () (unit type), making it a Has...]]></description><link>https://blog.iamdipankarpaul.com/understanding-hashset-in-rust</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/understanding-hashset-in-rust</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[beginner]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Fri, 14 Feb 2025 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/biBRoGc7ir0/upload/fb70029c54f62c25823b432d08cd5a80.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In Rust, a <code>HashSet</code> is a collection that stores unique elements in no particular order, using a hash-based implementation for efficient membership tests. Essentially, a <code>HashSet</code> is similar to a <code>HashMap</code> where the value is <code>()</code> (unit type), making it a HashMap with keys and no values.</p>
<h2 id="heading-creating-a-hashset">Creating a HashSet</h2>
<p>To use a <code>HashSet</code>, you need to include the <code>HashSet</code> module from the standard collections library.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::collections::HashSet;
</code></pre>
<p>You can create a <code>HashSet</code> with the <code>new</code> method and make it mutable for modifications.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> my_set = HashSet::new();
</code></pre>
<p>By default, the type is inferred based on the inserted values, but you can also specify types explicitly.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> my_set: HashSet&lt;<span class="hljs-built_in">String</span>&gt; = HashSet::new();
</code></pre>
<p>We can also create a hashset with default values using the <code>from()</code> method when creating it.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::collections::HashSet;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-comment">// Create HashSet with default values using from() method</span>
    <span class="hljs-keyword">let</span> numbers1 = HashSet::from([<span class="hljs-number">2</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">10</span>]);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"numbers = {:?}"</span>, numbers);
}
</code></pre>
<p>You can also turn a <code>Vec</code> into a <code>HashSet</code> using the <code>into_iter()</code> method(consumes ownership), followed by the <code>collect()</code> method.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::collections::HashSet;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-comment">// Turning a vector into a HashSet</span>
    <span class="hljs-keyword">let</span> my_vec: <span class="hljs-built_in">Vec</span>&lt;<span class="hljs-built_in">i32</span>&gt; = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];
    <span class="hljs-keyword">let</span> my_set: HashSet&lt;_&gt; = my_vec.into_iter().collect();
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{:?}"</span>, my_set);
}
</code></pre>
<p>Note that the type of the HashSet is inferred using the <code>_</code> placeholder, but you can explicitly specify the type if needed.</p>
<h2 id="heading-adding-elements-to-a-hashset">Adding Elements to a HashSet</h2>
<p>Elements can be added using the <code>insert</code> method, ensuring uniqueness within the set.</p>
<pre><code class="lang-rust">my_set.insert(<span class="hljs-string">"apple"</span>);
my_set.insert(<span class="hljs-string">"banana"</span>);
my_set.insert(<span class="hljs-string">"orange"</span>);
</code></pre>
<blockquote>
<p><strong>Note:</strong> Adding a new value to the hashset is only possible when you declare variable as <code>mut</code>.</p>
</blockquote>
<h2 id="heading-printing-a-hashset">Printing a HashSet</h2>
<p>To print the contents of a <code>HashSet</code>, use the <code>println!</code> macro with <code>{:?}</code> to display the elements.</p>
<pre><code class="lang-rust"><span class="hljs-built_in">println!</span>(<span class="hljs-string">"{:?}"</span>, my_set);
</code></pre>
<h2 id="heading-difference-between-hashset-and-btreeset">Difference Between HashSet and BTreeSet</h2>
<p>In Rust, there are different types of sets available. <code>HashSet</code> uses a hash table for fast access, suitable for unordered data. On the other hand, <code>BTreeSet</code> uses a binary search tree, maintaining elements in sorted order.</p>
<h2 id="heading-accessing-removing-and-updating-elements">Accessing, Removing, and Updating Elements</h2>
<p>You can check if an element is present using <code>contains</code>, remove elements with <code>remove</code>, and update elements with <code>replace</code>.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">if</span> my_set.contains(<span class="hljs-string">"apple"</span>) {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"The set contains 'apple'"</span>);
}

my_set.remove(<span class="hljs-string">"banana"</span>);
my_set.replace(<span class="hljs-string">"apple"</span>, <span class="hljs-string">"grape"</span>);
</code></pre>
<h2 id="heading-iterating-over-values-of-a-hashset">Iterating over Values of a HashSet</h2>
<p>There are several ways to iterate over the values of a <code>HashSet</code>, depending on whether you need references, mutable references, or want to consume and take ownership of the elements.</p>
<h3 id="heading-iterating-by-references-immutable-borrow">Iterating by References (Immutable Borrow)</h3>
<p>Using an immutable reference to iterate over the values of a <code>HashSet</code>.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::collections::HashSet;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> my_set: HashSet&lt;<span class="hljs-built_in">i32</span>&gt; = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>].into_iter().collect();

    <span class="hljs-keyword">for</span> value <span class="hljs-keyword">in</span> my_set.iter() {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Reference to: {}"</span>, value);
    }
}
</code></pre>
<p>Here, the <code>iter</code> method is used to obtain an iterator over references to the elements.</p>
<p>Using method chaining with closures,</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::collections::HashSet;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> my_set: HashSet&lt;<span class="hljs-built_in">i32</span>&gt; = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>].into_iter().collect();

    <span class="hljs-comment">// Using .iter().for_each() to print each element</span>
    my_set.iter().for_each(|&amp;value| {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Reference to: {}"</span>, value);
    });
}
</code></pre>
<p>Here, he closure takes an immutable reference to each element in the HashSet and prints it. These methods doesn't consume the elements, and ownership remains with the <code>HashSet</code>.</p>
<h3 id="heading-iterating-by-mutable-references-mutable-borrow">Iterating by Mutable References (Mutable Borrow)</h3>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::collections::HashSet;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> my_set: HashSet&lt;<span class="hljs-built_in">i32</span>&gt; = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>].into_iter().collect();

    <span class="hljs-keyword">for</span> value <span class="hljs-keyword">in</span> my_set.iter_mut() {
        *value *= <span class="hljs-number">2</span>;  <span class="hljs-comment">// Doubling each element in-place</span>
    }

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{:?}"</span>, my_set);
}
</code></pre>
<p>Here, the <code>iter_mut</code> method is used to obtain a mutable iterator, allowing modifications of the elements in the set.</p>
<p>Using method chaining with closures,</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::collections::HashSet;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> my_set: HashSet&lt;<span class="hljs-built_in">i32</span>&gt; = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>].into_iter().collect();

    my_set.iter_mut().for_each(|value| {
        *value *= <span class="hljs-number">2</span>;
    });

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{:?}"</span>, my_set);
}
</code></pre>
<p>Here, the closure takes a mutable reference to each element in the HashSet and doubles its value in-place. This allows modification of the elements in-place while still keeping ownership with the <code>HashSet</code>.</p>
<h3 id="heading-consuming-iteration-ownership-transfer">Consuming Iteration (Ownership Transfer)</h3>
<p>Using the <code>into_iter</code> method to consume the elements of the <code>HashSet</code>.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">for</span> value <span class="hljs-keyword">in</span> my_set.into_iter() {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Owned value: {}"</span>, value);
}
</code></pre>
<p>Here, <code>into_iter</code> is used to obtain an iterator that consumes the <code>HashSet</code>. The type of <code>value</code> is <code>i32</code>, indicating ownership transfer.</p>
<p>Using a <code>for</code> loop directly on the <code>HashSet</code> is concise but it will also consume and transfer ownership.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">for</span> value <span class="hljs-keyword">in</span> my_set {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Owned value: {}"</span>, value);
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p><code>HashSet</code> in Rust provides a powerful way to store unique elements efficiently. Understanding how to create, modify, and iterate over a <code>HashSet</code> is essential for working with collections in Rust.</p>
]]></content:encoded></item><item><title><![CDATA[Comprehensive Guide to HashMaps in Rust]]></title><description><![CDATA[Hash maps are a fundamental data structure in many programming languages, including Rust. They allow you to store data in key-value pairs, providing fast and efficient lookups based on keys. In Rust, hash maps are implemented in the std::collections:...]]></description><link>https://blog.iamdipankarpaul.com/comprehensive-guide-to-hashmaps-in-rust</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/comprehensive-guide-to-hashmaps-in-rust</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[beginner]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Fri, 31 Jan 2025 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/biBRoGc7ir0/upload/fb70029c54f62c25823b432d08cd5a80.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hash maps are a fundamental data structure in many programming languages, including Rust. They allow you to store data in key-value pairs, providing fast and efficient lookups based on keys. In Rust, hash maps are implemented in the <code>std::collections::HashMap</code> module. This guide will explore how to create, manipulate, and use hash maps in Rust, covering common operations, best practices, and potential pitfalls to avoid.</p>
<h2 id="heading-introduction-to-hashmaps">Introduction to HashMaps</h2>
<p>A HashMap is a collection of key-value pairs, where each key is unique. Keys are used to index the values, allowing for fast lookups, inserts, and removals. HashMaps are useful when you need to associate one piece of data with another, such as mapping usernames to user IDs or storing configuration settings.</p>
<h2 id="heading-creating-a-hashmap">Creating a HashMap</h2>
<p>To use HashMaps in Rust, you first need to import the <code>HashMap</code> type from the standard library:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::collections::HashMap;
</code></pre>
<p>You can then create a new, empty HashMap like so:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> my_map = HashMap::new();
</code></pre>
<p>The <code>mut</code> keyword is used to make the HashMap mutable, allowing you to add and remove elements.</p>
<h2 id="heading-adding-elements">Adding Elements</h2>
<p>You can add key-value pairs to a HashMap using the <code>insert</code> method:</p>
<pre><code class="lang-rust">my_map.insert(<span class="hljs-string">"key1"</span>, <span class="hljs-string">"value1"</span>);
my_map.insert(<span class="hljs-string">"key2"</span>, <span class="hljs-string">"value2"</span>);
</code></pre>
<p>Each call to <code>insert</code> adds a new key-value pair to the HashMap.</p>
<h2 id="heading-accessing-values">Accessing Values</h2>
<p>You can access values in a HashMap using their keys. Rust provides the <code>get</code> method, which returns an <code>Option&lt;&amp;V&gt;</code> (an optional reference to the value) where <code>V</code> is the type of the values in the HashMap:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(value) = my_map.get(<span class="hljs-string">"key1"</span>) {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Value for key1: {}"</span>, value);
}
</code></pre>
<p>This code prints the value associated with the key <code>"key1"</code> if it exists in the HashMap.</p>
<h2 id="heading-printing-hashmap">Printing HashMap</h2>
<p>The <code>{:?}</code> format specifier is used for printing the debug representation of the HashMap. However, remember that a hash map does not guarantee the order of its elements.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::collections::HashMap;

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">City</span></span> {
    name: <span class="hljs-built_in">String</span>,
    population: HashMap&lt;<span class="hljs-built_in">u32</span>, <span class="hljs-built_in">u32</span>&gt;, <span class="hljs-comment">// This will have the year and the population for the year</span>
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> my_map = City {
        name: <span class="hljs-string">"India"</span>.to_string(),
        population: HashMap::new(), <span class="hljs-comment">// empty HashMap</span>
    };

    my_map.population.insert(<span class="hljs-number">1372</span>, <span class="hljs-number">3_250</span>);
    my_map.population.insert(<span class="hljs-number">1851</span>, <span class="hljs-number">24_000</span>);
    my_map.population.insert(<span class="hljs-number">2020</span>, <span class="hljs-number">437_619</span>);


    <span class="hljs-keyword">for</span> (year, population) <span class="hljs-keyword">in</span> my_map.population {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{} {} {}."</span>, year, my_map.name, population);
    }
}
</code></pre>
<p>Output (1st run):</p>
<pre><code class="lang-rust"><span class="hljs-number">1851</span> India <span class="hljs-number">24000</span>.
<span class="hljs-number">2020</span> India <span class="hljs-number">437619</span>.
<span class="hljs-number">1372</span> India <span class="hljs-number">3250</span>.
</code></pre>
<p>Output (2nd run):</p>
<pre><code class="lang-rust"><span class="hljs-number">2020</span> India <span class="hljs-number">437619</span>.
<span class="hljs-number">1851</span> India <span class="hljs-number">24000</span>.
<span class="hljs-number">1372</span> India <span class="hljs-number">3250</span>.
</code></pre>
<p>You can see that it's not in order. If you want a <code>HashMap</code> that you can sort, you can use a <code>BTreeMap</code>.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::collections::BTreeMap; <span class="hljs-comment">// Just change HashMap to BTreeMap</span>

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">City</span></span> {
    name: <span class="hljs-built_in">String</span>,
    population: BTreeMap&lt;<span class="hljs-built_in">u32</span>, <span class="hljs-built_in">u32</span>&gt;, <span class="hljs-comment">// Just change HashMap to BTreeMap</span>
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {

    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> my_map = City {
        name: <span class="hljs-string">"India"</span>.to_string(),
        population: BTreeMap::new(), <span class="hljs-comment">// Just change HashMap to BTreeMap</span>
    };

    my_map.population.insert(<span class="hljs-number">1372</span>, <span class="hljs-number">3_250</span>);
    my_map.population.insert(<span class="hljs-number">1851</span>, <span class="hljs-number">24_000</span>);
    my_map.population.insert(<span class="hljs-number">2020</span>, <span class="hljs-number">437_619</span>);

    <span class="hljs-keyword">for</span> (year, population) <span class="hljs-keyword">in</span> my_map.population {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{} {} {}."</span>, year, my_map.name, population);
    }
}
</code></pre>
<p>Output:</p>
<pre><code class="lang-rust"><span class="hljs-number">1372</span> India <span class="hljs-number">3250</span>.
<span class="hljs-number">1851</span> India <span class="hljs-number">24000</span>.
<span class="hljs-number">2020</span> India <span class="hljs-number">437619</span>.
</code></pre>
<p>Now the output will be always in order.</p>
<h2 id="heading-removing-elements">Removing Elements</h2>
<p>To remove a key-value pair from a HashMap, you can use the <code>remove</code> method:</p>
<pre><code class="lang-rust">my_map.remove(<span class="hljs-string">"key2"</span>);
</code></pre>
<p>This code removes the key-value pair with the key <code>"key2"</code> from the HashMap.</p>
<h2 id="heading-updating-elements">Updating Elements</h2>
<p>If a HashMap already has a key when you try to put it in, it will overwrite its value.</p>
<pre><code class="lang-rust">my_map.insert(<span class="hljs-string">"key1"</span>, <span class="hljs-string">"new_value1"</span>);
</code></pre>
<p>This line updates the value associated with the key "key1" to "new_value1" in the HashMap. If the key already existed, the <code>insert</code> method returns the previous value. Otherwise, it returns <code>None</code>.</p>
<h2 id="heading-iterating-over-a-hashmap">Iterating Over a HashMap</h2>
<p>You can iterate over the key-value pairs in a HashMap using a <code>for</code> loop:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">for</span> (key, value) <span class="hljs-keyword">in</span> &amp;my_map {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Key: {}, Value: {}"</span>, key, value);
}
</code></pre>
<p>This code prints each key-value pair in the HashMap.</p>
<h2 id="heading-hashmap-usage">HashMap Usage</h2>
<ul>
<li><p>Fast Lookups: Hash maps provide fast and efficient lookups based on keys, with constant-time average-case complexity.</p>
</li>
<li><p>Uniqueness of Keys: Keys in a hash map must be unique, ensuring that each key corresponds to at most one value.</p>
</li>
<li><p>Associative Data Representation: Hash maps are well-suited for representing data with an associative relationship between keys and values, such as dictionaries.</p>
</li>
<li><p>Dynamic Size: Hash maps can dynamically grow or shrink based on the number of elements they contain, adapting to changing data sets.</p>
</li>
<li><p>Efficient Inserts and Removes: Hash maps offer efficient performance for inserting and removing key-value pairs, with constant-time average-case complexity.</p>
</li>
</ul>
<h2 id="heading-panic-situations">Panic Situations</h2>
<ul>
<li><p>Unordered Iteration: The order of items during iteration in a hash map is not guaranteed to be in any specific order, which can lead to unexpected behavior if order is relied upon.</p>
</li>
<li><p>Borrowing and Ownership: Care must be taken when borrowing values from a hash map during iteration to avoid borrowing issues, especially when modifying the hash map concurrently.</p>
</li>
<li><p>Missing Key Panics: Attempting to access a key that does not exist in a hash map using <code>get</code> and unwrapping the result may panic, necessitating careful handling of missing keys.</p>
</li>
<li><p>Limitations of Hashing Functions: Custom types used as keys in a hash map must have a properly implemented <code>Hash</code> trait to avoid incorrect behavior or panics.</p>
</li>
<li><p>Collision Handling: While rare, hash collisions can occur, where different keys produce the same hash value. Proper collision handling is necessary to ensure correct behavior in such cases.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>HashMaps are a powerful data structure in Rust, providing fast and efficient key-value storage and retrieval. By understanding how to create, modify, and use HashMaps, you can leverage their capabilities in your Rust programs.</p>
<p>HashMaps offer a dynamic and efficient way to manage data, making them suitable for a wide range of applications. Whether you're building a web server, implementing a caching mechanism, or simply need to store key-value pairs, HashMaps in Rust are a versatile tool that can help you achieve your goals.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Vectors in Rust: A Comprehensive Guide]]></title><description><![CDATA[In Rust, collections are fundamental data structures that allow you to store and manipulate multiple values efficiently. One such collection is the vector, a dynamically resizable array that is part of the standard library. Vectors provide flexibilit...]]></description><link>https://blog.iamdipankarpaul.com/understanding-vectors-in-rust-a-comprehensive-guide</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/understanding-vectors-in-rust-a-comprehensive-guide</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[beginner]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Tue, 14 Jan 2025 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/biBRoGc7ir0/upload/fb70029c54f62c25823b432d08cd5a80.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In Rust, collections are fundamental data structures that allow you to store and manipulate multiple values efficiently. One such collection is the vector, a dynamically resizable array that is part of the standard library. Vectors provide flexibility and performance for managing collections of elements.</p>
<h2 id="heading-introduction-to-vectors">Introduction to Vectors</h2>
<p>A vector in Rust is represented by the <code>Vec&lt;T&gt;</code> type, where <code>T</code> is the type of elements the vector will contain. Vectors are implemented as contiguous blocks of memory, allowing for efficient indexing and iteration. Unlike arrays, vectors can grow or shrink in size as needed, making them ideal for situations where the number of elements is not known at compile time or may change during runtime.</p>
<h2 id="heading-creating-and-initializing-vectors">Creating and Initializing Vectors</h2>
<p>You can create a new vector in Rust using the <code>Vec::new()</code> constructor or the <code>vec!</code> macro for initialization. Vectors can contain elements of any type, including integers, floats, strings, or even custom structs.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Using Vec::new() to create an empty vector</span>
<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> v1: <span class="hljs-built_in">Vec</span>&lt;<span class="hljs-built_in">i32</span>&gt; = <span class="hljs-built_in">Vec</span>::new();

<span class="hljs-comment">// Using vec! macro</span>
<span class="hljs-keyword">let</span> v2 = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];

<span class="hljs-comment">// Initializing a vector of strings</span>
<span class="hljs-keyword">let</span> names = <span class="hljs-built_in">vec!</span>[<span class="hljs-string">"Alice"</span>, <span class="hljs-string">"Bob"</span>, <span class="hljs-string">"Charlie"</span>];

<span class="hljs-comment">// Initializing a vector of custom structs</span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Person</span></span> {
    name: <span class="hljs-built_in">String</span>,
    age: <span class="hljs-built_in">u32</span>,
}

<span class="hljs-keyword">let</span> people = <span class="hljs-built_in">vec!</span>[
    Person { name: <span class="hljs-string">"Alice"</span>.to_string(), age: <span class="hljs-number">30</span> },
    Person { name: <span class="hljs-string">"Bob"</span>.to_string(), age: <span class="hljs-number">25</span> },
];
</code></pre>
<h2 id="heading-updating-vectors">Updating Vectors</h2>
<p>You can add elements to a vector using the <code>push</code> method. This method takes a single argument of the same type as the vector and appends it to the end of the vector.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> v = <span class="hljs-built_in">Vec</span>::new();
v.push(<span class="hljs-number">1</span>);
v.push(<span class="hljs-number">2</span>);
v.push(<span class="hljs-number">3</span>);
</code></pre>
<h2 id="heading-reading-elements-from-vectors">Reading Elements from Vectors</h2>
<p>You can access elements in a vector using indexing. Indexing starts at 0, so the first element is at index 0, the second element is at index 1, and so on.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> v = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">10</span>, <span class="hljs-number">20</span>, <span class="hljs-number">30</span>, <span class="hljs-number">40</span>, <span class="hljs-number">50</span>];
<span class="hljs-keyword">let</span> third_element = v[<span class="hljs-number">2</span>];
</code></pre>
<h2 id="heading-comparison-with-arrays">Comparison with Arrays</h2>
<p>Arrays and vectors are both used for storing collections of elements, but they have key differences in terms of size flexibility, ownership, and functionality:</p>
<h3 id="heading-arrays">Arrays:</h3>
<ul>
<li><p>Fixed size determined at compile time.</p>
</li>
<li><p>Allocated on the stack.</p>
</li>
<li><p>Entire array is mutable or immutable.</p>
</li>
</ul>
<h3 id="heading-vectors">Vectors:</h3>
<ul>
<li><p>Dynamic size that can grow or shrink.</p>
</li>
<li><p>Heap-allocated by default.</p>
</li>
<li><p>Individual elements can be mutable.</p>
</li>
</ul>
<h3 id="heading-commonalities">Commonalities:</h3>
<ul>
<li><p>Store elements of the same type.</p>
</li>
<li><p>Support indexing and iteration.</p>
</li>
</ul>
<h3 id="heading-decision-factors">Decision Factors:</h3>
<ul>
<li><p>Use arrays for fixed-size collections.</p>
</li>
<li><p>Use vectors for dynamic-size collections.</p>
</li>
</ul>
<p>Understanding the differences between arrays and vectors in Rust is crucial for choosing the right data structure for your needs. Vectors provide flexibility and efficiency for managing collections of elements, making them a powerful tool in Rust programming.</p>
<h2 id="heading-common-methods-of-vectors-in-rust">Common Methods of Vectors in Rust</h2>
<p>Vectors in Rust provide several useful methods for working with their contents. Here are some common methods:</p>
<ul>
<li><p><code>len()</code>: Returns the number of elements in the vector.</p>
</li>
<li><p><code>is_empty()</code>: Returns true if the vector is empty, false otherwise.</p>
</li>
<li><p><code>contains()</code>: Returns true if the vector contains a specific element, false otherwise.</p>
</li>
<li><p><code>iter()</code>: Returns an iterator over the vector's elements.</p>
</li>
<li><p><code>into_iter()</code>: Consumes the vector and returns an iterator over its elements.</p>
</li>
<li><p><code>push()</code>: Adds an element to the end of the vector.</p>
</li>
<li><p><code>pop()</code>: Removes and returns the last element of the vector.</p>
</li>
<li><p><code>remove()</code>: Removes the element at the specified index and returns it.</p>
</li>
<li><p><code>get()</code>: Returns a reference to the element at the specified index, or None if the index is out of bounds.</p>
</li>
<li><p><code>get_mut()</code>: Returns a mutable reference to the element at the specified index, or None if the index is out of bounds.</p>
</li>
<li><p><code>sort()</code>: Sorts the elements of the vector in ascending order.</p>
</li>
<li><p><code>reverse()</code>: Reverses the order of the elements in the vector.</p>
</li>
<li><p><code>clear()</code>: Clears the vector, removing all values.</p>
</li>
</ul>
<p>These methods provide a powerful set of tools for working with vectors in Rust, allowing you to manipulate their contents in various ways.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, vectors are versatile and powerful data structures in Rust that allow you to store and manipulate collections of elements efficiently. By understanding how vectors work and how to use them effectively, you can write more expressive and efficient Rust code.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Strings in Rust]]></title><description><![CDATA[When working with text in Rust, it's essential to understand the two primary string types: String and str. In this guide, we'll explore the differences between them and how to use them effectively.
Introduction to Strings
A string is a collection of ...]]></description><link>https://blog.iamdipankarpaul.com/understanding-strings-in-rust</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/understanding-strings-in-rust</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[beginner]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Wed, 01 Jan 2025 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/biBRoGc7ir0/upload/fb70029c54f62c25823b432d08cd5a80.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When working with text in Rust, it's essential to understand the two primary string types: <code>String</code> and <code>str</code>. In this guide, we'll explore the differences between them and how to use them effectively.</p>
<h2 id="heading-introduction-to-strings">Introduction to Strings</h2>
<p>A string is a collection of characters. In Rust, the <code>String</code> type represents a growable, mutable, owned, heap-allocated UTF-8 encoded string. On the other hand, <code>str</code> (string slice) is an immutable view into a sequence of UTF-8 bytes, usually borrowed from a <code>String</code> or a string literal.</p>
<h2 id="heading-string-type-string">String Type (<code>String</code>)</h2>
<p>A <code>String</code> in Rust is a mutable, UTF-8 encoded string that can grow and shrink in size. You can create a <code>String</code> from a string literal or from another <code>String</code> using the <code>to_string</code> method.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> s1: <span class="hljs-built_in">String</span> = <span class="hljs-string">"Hello, "</span>.to_string();
<span class="hljs-keyword">let</span> s2: <span class="hljs-built_in">String</span> = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"world!"</span>);
<span class="hljs-keyword">let</span> s3: <span class="hljs-built_in">String</span> = <span class="hljs-built_in">String</span>::new(); <span class="hljs-comment">// Dynamic empty string</span>
<span class="hljs-keyword">let</span> combined = s1 + &amp;s2;

<span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, combined); <span class="hljs-comment">// Prints: Hello, world!</span>
</code></pre>
<p>One other way to make a <code>String</code> is called <code>.into()</code> but it is a bit different because <code>.into()</code> isn't just for making a <code>String</code>.</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> s4 = <span class="hljs-string">"Try to make this a String"</span>.into(); 
    <span class="hljs-comment">// ⚠️type annotations needed, consider giving `s4` a type</span>

    <span class="hljs-keyword">let</span> s5: <span class="hljs-built_in">String</span> = <span class="hljs-string">"Try to make this a String"</span>.into();
    <span class="hljs-comment">// ✅ Now compiler is happy with `String` type</span>
}
</code></pre>
<h3 id="heading-methods-on-string">Methods on <code>String</code></h3>
<p><code>String</code> has various methods for manipulating and working with strings. Some common methods include:</p>
<ul>
<li><p><code>push_str</code>: Appends a string slice to the end of the <code>String</code>.</p>
</li>
<li><p><code>replace</code>: Replaces a substring with another substring.</p>
</li>
<li><p><code>to_uppercase</code>/<code>to_lowercase</code>: Converts the string to uppercase or lowercase.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> my_string = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Hello, world!"</span>);
my_string.push_str(<span class="hljs-string">" How are you?"</span>);
my_string = my_string.replace(<span class="hljs-string">"world"</span>, <span class="hljs-string">"Rust"</span>);

<span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, my_string.to_uppercase()); 
<span class="hljs-comment">// Prints: HELLO, RUST! HOW ARE YOU?</span>
</code></pre>
<h2 id="heading-string-slice-ampstr">String Slice (<code>&amp;str</code>)</h2>
<p>A <code>str</code> is an immutable reference to a sequence of UTF-8 bytes. It is usually seen as a borrowed form of a <code>String</code> or a string literal.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> s: &amp;<span class="hljs-built_in">str</span> = <span class="hljs-string">"Hello, world!"</span>;
<span class="hljs-keyword">let</span> slice: &amp;<span class="hljs-built_in">str</span> = &amp;s[<span class="hljs-number">0</span>..<span class="hljs-number">5</span>]; <span class="hljs-comment">// Take a slice from index 0 to 5 (exclusive)</span>

<span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, slice); <span class="hljs-comment">// Prints: Hello</span>
</code></pre>
<p>You can even write emojis, thanks to UTF-8.</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> name = <span class="hljs-string">"😂"</span>;
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"My name is actually {}"</span>, name);
    <span class="hljs-comment">// prints: My name is actually 😂</span>
}
</code></pre>
<h3 id="heading-methods-on-str">Methods on <code>str</code></h3>
<p><code>str</code> also has a variety of methods for manipulating and working with text. Some common methods include:</p>
<ul>
<li><p><code>len</code>: Returns the length of the string.</p>
</li>
<li><p><code>is_empty</code>: Returns true if the string is empty.</p>
</li>
<li><p><code>starts_with</code>/<code>ends_with</code>: Checks if the string starts or ends with a given substring.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> my_str: &amp;<span class="hljs-built_in">str</span> = <span class="hljs-string">"Hello, Rust!"</span>;
<span class="hljs-keyword">let</span> my_string: <span class="hljs-built_in">String</span> = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Hello, Rust!"</span>);

<span class="hljs-built_in">println!</span>(<span class="hljs-string">"str Length: {}"</span>, my_str.len());
<span class="hljs-built_in">println!</span>(<span class="hljs-string">"String Length: {}"</span>, my_string.len());

<span class="hljs-built_in">println!</span>(<span class="hljs-string">"str Is empty: {}"</span>, my_str.is_empty());
<span class="hljs-built_in">println!</span>(<span class="hljs-string">"String Is empty: {}"</span>, my_string.is_empty());

<span class="hljs-built_in">println!</span>(<span class="hljs-string">"str Starts with 'Hello': {}"</span>, my_str.starts_with(<span class="hljs-string">"Hello"</span>));
<span class="hljs-built_in">println!</span>(<span class="hljs-string">"String Starts with 'Hello': {}"</span>, my_string.starts_with(<span class="hljs-string">"Hello"</span>));
</code></pre>
<h2 id="heading-transforming-between-string-and-str">Transforming Between <code>String</code> and <code>str</code></h2>
<p>You can convert a <code>String</code> to a <code>&amp;str</code> using the <code>as_str</code> method or by using the <code>&amp;</code> operator. Conversely, you can convert a <code>&amp;str</code> to <code>String</code> using the <code>to_string</code> method.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> my_string: <span class="hljs-built_in">String</span> = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Hello"</span>);
<span class="hljs-keyword">let</span> my_str1: &amp;<span class="hljs-built_in">str</span> = my_string.as_str();
<span class="hljs-keyword">let</span> my_str2: &amp;<span class="hljs-built_in">str</span> = &amp;my_string;

<span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, my_str1); <span class="hljs-comment">// Prints: Hello</span>
<span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, my_str2); <span class="hljs-comment">// Prints: Hello</span>

<span class="hljs-keyword">let</span> my_str: &amp;<span class="hljs-built_in">str</span> = <span class="hljs-string">"World!"</span>;
<span class="hljs-keyword">let</span> my_string: <span class="hljs-built_in">String</span> = my_str.to_string();

<span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, my_string); <span class="hljs-comment">// Prints: World!</span>
</code></pre>
<h2 id="heading-concatenating-strings">Concatenating Strings</h2>
<p>There are several ways to concatenate strings in Rust, depending on your needs:</p>
<ul>
<li><p>Using the <code>+</code> operator (consumes ownership of the first string).</p>
</li>
<li><p>Using the <code>format!</code> macro (does not consume ownership of any string).</p>
</li>
<li><p>Using the <code>push_str</code> method to append a string slice to a <code>String</code>.</p>
</li>
<li><p>Using the <code>push</code> method to append a single character or string slice to a <code>String</code>.</p>
</li>
</ul>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> s1 = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Hello, "</span>);
<span class="hljs-keyword">let</span> s2 = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"world!"</span>);

<span class="hljs-keyword">let</span> combined = s1 + &amp;s2; <span class="hljs-comment">// Using the + operator</span>

<span class="hljs-keyword">let</span> combined = <span class="hljs-built_in">format!</span>(<span class="hljs-string">"{}{}"</span>, s1, s2); <span class="hljs-comment">// Using the format! macro</span>

<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> s1 = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Hello,"</span>);
<span class="hljs-keyword">let</span> s2 = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"world!"</span>);

s1.push_str(&amp;s2); <span class="hljs-comment">// Using the push_str method</span>

<span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> s1 = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Hello,"</span>);
<span class="hljs-keyword">let</span> s2 = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"world!"</span>);

s1.push(<span class="hljs-string">' '</span>); <span class="hljs-comment">// Adding a space(' ') Char type</span>
s1.push_str(&amp;s2); <span class="hljs-comment">// Using the push method</span>
</code></pre>
<p>When concatenating strings, be aware of ownership and borrowing rules to avoid issues like moving ownership unintentionally. If you need to concatenate strings in a loop or multiple times, consider using a <code>String</code> and appending with <code>push_str</code> for better performance.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Understanding the differences between <code>String</code> and <code>str</code> is crucial for writing efficient and safe Rust code when working with strings. By following Rust's ownership and borrowing rules, you can manipulate strings effectively while ensuring memory safety.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Closures in Rust]]></title><description><![CDATA[Closures in Rust are powerful constructs that allow you to define anonymous functions with the ability to capture variables from their surrounding environment. They are particularly useful for situations where you need to pass behavior around as argu...]]></description><link>https://blog.iamdipankarpaul.com/understanding-closures-in-rust</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/understanding-closures-in-rust</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[beginner]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Sat, 14 Dec 2024 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/biBRoGc7ir0/upload/fb70029c54f62c25823b432d08cd5a80.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Closures in Rust are powerful constructs that allow you to define anonymous functions with the ability to capture variables from their surrounding environment. They are particularly useful for situations where you need to pass behavior around as arguments to other functions or store them in data structures. In this guide, we'll explore the syntax and usage of closures in Rust, along with their capturing modes and practical examples.</p>
<h2 id="heading-what-are-closures">What are Closures?</h2>
<p>In Rust, closures are defined using the <code>|args| body</code> syntax, where <code>args</code> inside pipe symbols <code>|</code> represent the parameters and <code>body</code> represents the implementation. Closures can capture variables from their enclosing scope, making them flexible and concise.</p>
<h2 id="heading-basic-syntax-of-a-closure">Basic Syntax of a Closure</h2>
<p>A basic closure in Rust looks like this:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> print_text = || <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Hello World!"</span>);
</code></pre>
<p>Here, <code>print_text</code> is a variable that stores a closure. The <code>||</code> indicates the start of the closure, and <code>println!("Hello World!")</code> is the body of the closure.</p>
<h2 id="heading-calling-a-closure">Calling a Closure</h2>
<p>Closures can be called just like regular functions.</p>
<pre><code class="lang-rust">print_text(); <span class="hljs-comment">// Output: Hello World!</span>
</code></pre>
<h2 id="heading-closure-with-parameters">Closure with Parameters</h2>
<p>Closures can take parameters just like functions.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> multiply = |x, y| x * y;
<span class="hljs-keyword">let</span> result = multiply(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>);
<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Result: {}"</span>, result); <span class="hljs-comment">// Output: Result: 12</span>
</code></pre>
<h2 id="heading-capturing-variables-with-closures">Capturing Variables with Closures</h2>
<p>Closures in Rust capture their environment by reference by default, allowing them to access variables from the scope in which they are defined. This behavior enables closures to carry context with them.</p>
<p>Closures can capture variables in different modes: <code>Fn</code>, <code>FnMut</code>, and <code>FnOnce</code>. These modes determine how the closure interacts with the variables it captures.</p>
<h3 id="heading-fn-immutable-borrowing">Fn (Immutable Borrowing)</h3>
<p>Borrows variables by reference, allowing the closure to read the variables but not modify them. The closure retains a reference to the captured variables, allowing multiple invocations without altering the original values.</p>
<p>Example:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> x = <span class="hljs-number">5</span>;

<span class="hljs-comment">// immutable closure</span>
<span class="hljs-keyword">let</span> print_x = || <span class="hljs-built_in">println!</span>(<span class="hljs-string">"x: {}"</span>, x);

print_x(); <span class="hljs-comment">// Output: x: 5</span>
print_x(); <span class="hljs-comment">// Output: x: 5</span>
</code></pre>
<p>In this example, the closure <code>print_x</code> captures the variable <code>x</code> using the <code>Fn</code> mode. It can access <code>x</code> and print its value, but it cannot modify <code>x</code>.</p>
<p>This mode of capture is also known as <strong>Capture by Immutable Borrow</strong>.</p>
<h3 id="heading-fnmut-mutable-borrowing">FnMut (Mutable Borrowing)</h3>
<p>Borrows variables by mutable reference, allowing the closure to modify the captured variables.</p>
<p>Example:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> x = <span class="hljs-number">5</span>;

    <span class="hljs-comment">// mutable closure</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> increment_x = || {
        x += <span class="hljs-number">1</span>;
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Incremented x: {}"</span>, x);
    };

    increment_x(); <span class="hljs-comment">// Incremented x: 6</span>
    increment_x(); <span class="hljs-comment">// Incremented x: 7</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"x: {}"</span>, x); <span class="hljs-comment">// x: 7</span>
}
</code></pre>
<p>In this example, the mutable closure <code>increment_x</code> captures the variable <code>x</code> using the <code>FnMut</code> mode. It can modify <code>x</code>, incrementing its value, and print the updated value.</p>
<p>Mutable closure means no other references of the variable <code>x</code> can exist unless the closure is used. For example,</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> x = <span class="hljs-number">5</span>;

    <span class="hljs-comment">// mutable closure/ mutable borrow</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> increment_x = || {
        x += <span class="hljs-number">1</span>;
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Incremented x: {}"</span>, x);
    };

    <span class="hljs-comment">// Uncommenting this line will give an error</span>
    <span class="hljs-comment">// let y = &amp;x; // immutable borrow</span>
    <span class="hljs-comment">// Error: cannot borrow `x` as immutable because it is also borrowed as mutable</span>

    increment_x(); <span class="hljs-comment">// Output: Incremented x: 6</span>

    <span class="hljs-keyword">let</span> z = &amp;x; <span class="hljs-comment">// No error, as mutable closure is already used</span>
}
</code></pre>
<p>This mode of capture is also known as <strong>Capture by Mutable Borrow</strong>.</p>
<h3 id="heading-fnonce-consuming">FnOnce (Consuming)</h3>
<p>Consumes variables, taking ownership of them and allowing only a single call to the closure.</p>
<p>Example:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> x = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
<span class="hljs-keyword">let</span> print_and_consume_x = || {
    <span class="hljs-keyword">let</span> new_x = x; <span class="hljs-comment">// Move happens here</span>
    <span class="hljs-comment">// this value implements `FnOnce`, which causes it to be moved when called</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Consumed x: {:?}"</span>, new_x);
};
<span class="hljs-comment">// x has been consumed and cannot be used again</span>

print_and_consume_x(); <span class="hljs-comment">// Output: Consumed x: [1, 2, 3]</span>
<span class="hljs-comment">// print_and_consume_x(); // Uncommenting this line will give an error</span>
<span class="hljs-comment">// Error: closure cannot be invoked more than once </span>
<span class="hljs-comment">// because it moves the variable `x` out of its environment</span>
<span class="hljs-comment">// println!("x: {:?}", x); // Error: value borrowed here after move</span>
</code></pre>
<p>In this example, the closure <code>print_and_consume_x</code> captures the variable <code>x</code> using the <code>FnOnce</code> mode. Then it moves the variable <code>x</code> to a new variable <code>new_x</code> inside the closure. It prints the contents of <code>new_x</code> and consumes it, preventing any further use of <code>x</code> after the first invocation.</p>
<p>This mode of capture is also known as <strong>Capture by Move</strong>.</p>
<h2 id="heading-move-keyword"><code>move</code> Keyword</h2>
<p>The <code>move</code> keyword is used with closures to explicitly specify how variables should be captured from the enclosing scope. By default, closures in Rust capture variables by reference, but using <code>move</code> changes this behavior to allow the closure to take ownership of the captured variables. This can be particularly useful in situations where you want the closure to own the data it captures, enabling the closure to outlive the scope in which it was defined.</p>
<p>Example:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> name = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Alice"</span>);
    <span class="hljs-keyword">let</span> greet = <span class="hljs-keyword">move</span> || {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Hello, {}!"</span>, name);
    };
    greet();
    <span class="hljs-comment">// println!("Name: {}", name); // Error: value moved</span>
}
</code></pre>
<p>In this example, the closure <code>greet</code> captures the <code>name</code> variable using the <code>move</code> keyword. This means the closure takes ownership of the <code>name</code> variable, allowing it to be used inside the closure. However, once the closure is defined, the <code>name</code> variable is no longer accessible in the outer scope because it has been moved into the closure.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Closures in Rust are powerful tools for defining flexible and concise behavior. Understanding how to define and use closures, along with their capturing modes, is essential for writing clean and expressive Rust code. Also by using <code>move</code>, you can explicitly specify that a closure should take ownership of its captured variables, enabling you to safely use the closure in different contexts and ensuring that the data it captures remains valid for the duration of its lifetime.</p>
]]></content:encoded></item><item><title><![CDATA[Slices in Rust: A Comprehensive Guide]]></title><description><![CDATA[Rust, renowned for its emphasis on safety and performance, offers a versatile data type known as slices. Slices provide a means to access portions of data stored in collections like arrays, vectors, and strings, without the overhead of ownership. In ...]]></description><link>https://blog.iamdipankarpaul.com/slices-in-rust-a-comprehensive-guide</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/slices-in-rust-a-comprehensive-guide</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[beginner]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Sat, 30 Nov 2024 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/biBRoGc7ir0/upload/fb70029c54f62c25823b432d08cd5a80.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Rust, renowned for its emphasis on safety and performance, offers a versatile data type known as slices. Slices provide a means to access portions of data stored in collections like arrays, vectors, and strings, without the overhead of ownership. In this guide, we'll delve into the intricacies of slices, exploring their syntax, applications, and examples.</p>
<h2 id="heading-understanding-the-slice-type">Understanding the Slice Type</h2>
<p>In Rust, a slice represents a reference to a contiguous sequence of elements within a collection. Unlike some other data types, slices do not own the data they reference, making them lightweight and efficient.</p>
<h3 id="heading-slices-with-arrays">Slices with Arrays</h3>
<p>Let's begin by examining how slices operate with arrays:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> my_array = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];

    <span class="hljs-comment">// Creating a slice from the array</span>
    <span class="hljs-keyword">let</span> my_slice = &amp;my_array[<span class="hljs-number">1</span>..<span class="hljs-number">4</span>]; 
    <span class="hljs-comment">// Takes elements from index 1 to 3 (4 is exclusive)</span>

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Original Array: {:?}"</span>, my_array); <span class="hljs-comment">// [1, 2, 3, 4, 5]</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Slice: {:?}"</span>, my_slice); <span class="hljs-comment">// [2, 3, 4]</span>
}
</code></pre>
<p>In this example, <code>my_slice</code> references elements 2, 3, and 4 from <code>my_array</code>.</p>
<h3 id="heading-omitting-indexes-of-a-rust-slice">Omitting Indexes of a Rust Slice</h3>
<p>Rust offers flexibility in specifying slices by allowing the omission of start and end indexes:</p>
<p>Syntax:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> slice = &amp;var[start_index..end_index]; <span class="hljs-comment">// start from `start_index` and goes up to `end_index`(exclusive)</span>
<span class="hljs-keyword">let</span> slice = &amp;var[start_index..=end_index]; <span class="hljs-comment">// start from `start_index` and goes up to `end_index`(inclusive)</span>
</code></pre>
<h4 id="heading-omitting-the-start-index-of-a-slice">Omitting the Start Index of a Slice</h4>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> slice = &amp;var[..<span class="hljs-number">3</span>];
</code></pre>
<p>This indicates the slice starts from index 0 and extends to index 3 (exclusive).</p>
<h4 id="heading-omitting-the-end-index-of-a-slice">Omitting the End Index of a Slice</h4>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> slice = &amp;var[<span class="hljs-number">2</span>..];
</code></pre>
<p>This signifies the slice starts from index 2 and spans till the end of the collection.</p>
<h4 id="heading-omitting-both-start-and-end-index-of-a-slice">Omitting both Start and End Index of a Slice</h4>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> slice = &amp;var[..];
</code></pre>
<p>This denotes the slice covers the entire collection.</p>
<h2 id="heading-mutable-slices">Mutable Slices</h2>
<p>Mutable slices allow for in-place modification of data within a collection:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-comment">// mutable array</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> colors = [<span class="hljs-string">"red"</span>, <span class="hljs-string">"green"</span>, <span class="hljs-string">"yellow"</span>, <span class="hljs-string">"white"</span>];

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"original array = {:?}"</span>, colors);

    <span class="hljs-comment">// mutable slice</span>
    <span class="hljs-keyword">let</span> sliced_colors = &amp;<span class="hljs-keyword">mut</span> colors[<span class="hljs-number">1</span>..<span class="hljs-number">3</span>];
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"original slice = {:?}"</span>, sliced_colors); <span class="hljs-comment">// ["green", "yellow"]</span>

    <span class="hljs-comment">// change the value of the original slice at the first index</span>
    sliced_colors[<span class="hljs-number">1</span>] = <span class="hljs-string">"purple"</span>;

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"changed slice = {:?}"</span>, sliced_colors); <span class="hljs-comment">// ["green", "purple"]</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"changed array = {:?}"</span>, colors); <span class="hljs-comment">// ["red", "green", "purple", "white"]</span>
}
</code></pre>
<p>Here, <code>sliced_colors</code> references a portion of the <code>colors</code> array, enabling direct modification of its elements.</p>
<h2 id="heading-slices-with-strings">Slices with Strings</h2>
<p>Slices extend their utility to strings as well:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> my_string = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Hello, Rust!"</span>);

    <span class="hljs-comment">// Creating a slice from the string</span>
    <span class="hljs-keyword">let</span> my_slice = &amp;my_string[<span class="hljs-number">7</span>..<span class="hljs-number">11</span>]; 
    <span class="hljs-comment">// Takes characters from index 7 to 10 (11 is exclusive)</span>

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Original String: {}"</span>, my_string); <span class="hljs-comment">// Hello, Rust!</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Slice: {}"</span>, my_slice); <span class="hljs-comment">// Rust</span>
}
</code></pre>
<p>In this example, <code>my_slice</code> captures the substring "Rust" from <code>my_string</code>.</p>
<h2 id="heading-practical-applications-of-slices">Practical Applications of Slices</h2>
<h3 id="heading-avoiding-unnecessary-copying">Avoiding Unnecessary Copying</h3>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">sum_elements</span></span>(data: &amp;[<span class="hljs-built_in">i32</span>]) -&gt; <span class="hljs-built_in">i32</span> {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> sum = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">for</span> &amp;num <span class="hljs-keyword">in</span> data {
        sum += num;
    }
    sum
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> my_array = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];

    <span class="hljs-comment">// Passing only the required slice</span>
    <span class="hljs-keyword">let</span> total = sum_elements(&amp;my_array[<span class="hljs-number">1</span>..<span class="hljs-number">4</span>]);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Total: {}"</span>, total);
}
</code></pre>
<p>By leveraging slices, unnecessary copying of entire collections is mitigated, enhancing efficiency.</p>
<h3 id="heading-sorting-part-of-an-array">Sorting Part of an Array</h3>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> numbers = [<span class="hljs-number">5</span>, <span class="hljs-number">2</span>, <span class="hljs-number">8</span>, <span class="hljs-number">1</span>, <span class="hljs-number">9</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>];

    <span class="hljs-comment">// Sort a portion of the array using a slice</span>
    numbers[<span class="hljs-number">2</span>..<span class="hljs-number">5</span>].sort();
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Sorted Array: {:?}"</span>, numbers); <span class="hljs-comment">// [5, 2, 1, 8, 9, 3, 7]</span>
}
</code></pre>
<p>Mutable slices facilitate sorting only a subset of an array, demonstrating their versatility.</p>
<h2 id="heading-where-slices-should-be-used">Where Slices Should be Used:</h2>
<ul>
<li><p><strong>Avoiding Unnecessary Copying:</strong><br />  Slices are useful when you only need to work with a part of a collection without copying the data. This is more efficient than creating a new collection with the same elements.</p>
</li>
<li><p><strong>Passing Subsets of Data:</strong><br />  Slices are commonly used when passing parts of a collection to functions. Instead of passing the entire collection, you can pass a reference to a slice.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Slices are indispensable tools in Rust programming, offering a lightweight means to manipulate portions of data within collections efficiently. By understanding their syntax and applications, developers can leverage slices to enhance code readability, performance, and maintainability, ultimately unlocking the full potential of Rust's robust ecosystem.</p>
]]></content:encoded></item><item><title><![CDATA[Mastering Rust References and Borrowing: Safely Navigating Memory]]></title><description><![CDATA[One of the core strengths of Rust lies in its approach to memory management, particularly through its ownership system. A crucial aspect of this system is the use of references and borrowing, which allow for safe and efficient manipulation of data wi...]]></description><link>https://blog.iamdipankarpaul.com/mastering-rust-references-and-borrowing-safely-navigating-memory</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/mastering-rust-references-and-borrowing-safely-navigating-memory</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[beginner]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Fri, 15 Nov 2024 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/biBRoGc7ir0/upload/fb70029c54f62c25823b432d08cd5a80.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One of the core strengths of Rust lies in its approach to memory management, particularly through its ownership system. A crucial aspect of this system is the use of references and borrowing, which allow for safe and efficient manipulation of data without the risks of memory leaks or data races. In this article, we'll delve into the intricacies of references and borrowing in Rust, exploring their types, rules, and best practices through examples.</p>
<h2 id="heading-references-in-rust">References in Rust</h2>
<p>References in Rust allow us to point to a value without taking ownership of it. By creating references, we can enable multiple parts of our code to access the same data without introducing concurrency issues or memory leaks.</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">calculate_length</span></span>(s: &amp;<span class="hljs-built_in">String</span>) -&gt; <span class="hljs-built_in">usize</span> {
    s.len()
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-built_in">str</span> = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Hello, World!"</span>);

    <span class="hljs-comment">// Calling a function with a reference to a String value</span>
    <span class="hljs-keyword">let</span> len = calculate_length(&amp;<span class="hljs-built_in">str</span>);

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"The length of '{str}' is {len}."</span>);
    <span class="hljs-comment">// prints → The length of 'Hello, World!' is 13.</span>
}
</code></pre>
<p>In this example, <code>calculate_length()</code> takes a reference to a <code>String</code> as a parameter. This reference, denoted by <code>&amp;String</code>, allows the function to access the value of <code>str</code> without taking ownership of it. This concept of referencing is fundamental to Rust's ownership model, ensuring memory safety and efficient resource management.</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">calculate_length</span></span>(s: &amp;<span class="hljs-built_in">String</span>) -&gt; <span class="hljs-built_in">usize</span> {
    s.len()
}
</code></pre>
<p>Here, <code>s</code> goes out of scope, at the end of the function <code>calculate_length()</code>, but it is not dropped because it does not have ownership of what it refers to.</p>
<p><strong>The action of creating a reference is known as borrowing</strong>. Borrowing is when we borrow something, and when we are done with it, we give it back. It doesn't make us the owner of the data.</p>
<blockquote>
<p><strong>Note:</strong> Ampersand (<code>&amp;</code>) represents references, and they allow us to refer to some value without taking ownership of it.</p>
<p>The opposite of referencing by using <code>&amp;</code> is <em>dereferencing</em>, which is accomplished with the dereference operator, <code>*</code>.</p>
</blockquote>
<h2 id="heading-types-of-references">Types of References</h2>
<p>Rust supports two types of references:</p>
<ul>
<li><p><strong>Immutable References (</strong><code>&amp;T</code>): Allow read-only access to the data.</p>
</li>
<li><p><strong>Mutable References (</strong><code>&amp;mut T</code>): Allow read-write access, with strict rules to prevent data races.</p>
</li>
</ul>
<h2 id="heading-modifying-references">Modifying References</h2>
<p>While references are typically immutable by default, Rust allows us to create mutable references when needed. These mutable references enable us to modify the data they reference, within certain constraints.</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> <span class="hljs-built_in">str</span> = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Hello"</span>);

    <span class="hljs-comment">// Before modifying the string</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Before: str = {}"</span>, <span class="hljs-built_in">str</span>); <span class="hljs-comment">// prints =&gt; Hello</span>

    <span class="hljs-comment">// Passing a mutable string when calling the function</span>
    change(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-built_in">str</span>);

    <span class="hljs-comment">// After modifying the string</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"After: str = {}"</span>, <span class="hljs-built_in">str</span>); <span class="hljs-comment">// prints =&gt; Hello, World!</span>
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">change</span></span>(s: &amp;<span class="hljs-keyword">mut</span> <span class="hljs-built_in">String</span>) {
    <span class="hljs-comment">// Appending to the mutable reference variable</span>
    s.push_str(<span class="hljs-string">", World!"</span>);
}
</code></pre>
<p>In this example, the <code>change()</code> function accepts a mutable reference to a <code>String</code> and appends a string to it. This demonstrates how mutable references enable us to modify data in a safe and controlled manner.</p>
<h2 id="heading-rules-for-references">Rules for References</h2>
<p>Rust imposes strict rules for working with references to ensure memory safety and prevent data races. Some key rules include:</p>
<ul>
<li><p>You can have many immutable references or one mutable reference, but not both at the same time.</p>
</li>
<li><p>References must always be valid for the duration of their use i.e. the data they point to must outlive the reference.</p>
</li>
<li><p>Preventing modifications through immutable references and enforcing exclusive access with mutable references.</p>
</li>
</ul>
<h3 id="heading-example-of-multiple-immutable-reference">Example of multiple immutable reference</h3>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> x = <span class="hljs-number">42</span>;

    <span class="hljs-comment">// Creating multiple immutable references</span>
    <span class="hljs-keyword">let</span> ref1 = &amp;x;
    <span class="hljs-keyword">let</span> ref2 = &amp;x;

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Value of x: {}"</span>, x); <span class="hljs-comment">// 42</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Value of reference1: {}"</span>, ref1); <span class="hljs-comment">// 42</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Value of reference2: {}"</span>, *ref2); <span class="hljs-comment">// 42</span>
}
</code></pre>
<p>It's important to note that multiple immutable references are permitted because they don't introduce the risk of <em>data races</em>. If any of these references were trying to modify the data, the Rust compiler would catch it at compile time.</p>
<blockquote>
<p><strong>Note:</strong> The dereference operator <code>*</code> is used to access the value that a reference is pointing to. When you have a reference, you use the <code>*</code> operator to <strong>"dereference"</strong> it and access the actual value it refers to.</p>
</blockquote>
<h3 id="heading-example-of-multiple-mutable-reference">Example of multiple mutable reference</h3>
<p>If you have a mutable reference to a value, you can have no other references to that value.</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> <span class="hljs-built_in">str</span> = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"hello"</span>);

    <span class="hljs-comment">// mutable reference 1</span>
    <span class="hljs-keyword">let</span> ref1 = &amp;<span class="hljs-keyword">mut</span> <span class="hljs-built_in">str</span>;

    <span class="hljs-comment">// mutable reference 2</span>
    <span class="hljs-keyword">let</span> ref2 = &amp;<span class="hljs-keyword">mut</span> <span class="hljs-built_in">str</span>; 
    <span class="hljs-comment">// Error: cannot borrow `str` as mutable more than once at a time</span>

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}, {}"</span>, ref1, ref2);
}
</code></pre>
<p>Rust's rule against having multiple mutable references to the same data(variable) at the same time is like a safety measure. It means you can change data, but in a careful way. The cool part is that Rust can catch and stop potential issues called data races before your code even runs.</p>
<p>A data race is a problem when:</p>
<ol>
<li><p>Two or more pointers are trying to look at the same data at the exact same time.</p>
</li>
<li><p>At least one of these pointers is trying to change the data.</p>
</li>
<li><p>There's no plan(mechanism) to organize how these changes happen.</p>
</li>
</ol>
<p>Data races can mess things up really badly and are tricky to find and fix when you run your program. Rust saves you from this headache by not letting you compile code that could cause data races!</p>
<p>It's also important to note that the dereference operator <code>*</code> is can be used when working with mutable references to modify the data. For example,</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
   <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> y = <span class="hljs-number">5</span>;
   <span class="hljs-comment">// Creating a mutable reference to the value of y</span>
   <span class="hljs-keyword">let</span> reference = &amp;<span class="hljs-keyword">mut</span> y;
   <span class="hljs-comment">// Using the dereference operator to modify the value</span>
   *reference += <span class="hljs-number">10</span>;
   <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Updated value of y: {}"</span>, y);
}
</code></pre>
<h3 id="heading-example-of-both-references-at-the-same-time">Example of both references at the same time</h3>
<p>You can't have both mutable and immutable references to the same data at the same time. This is because allowing both would risk unexpected changes to the data. Rust enforces this rule to ensure that code remains predictable and safe.</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> s = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"hello"</span>);

    <span class="hljs-keyword">let</span> r1 = &amp;s;     <span class="hljs-comment">// No problem, immutable reference</span>
    <span class="hljs-keyword">let</span> r2 = &amp;s;     <span class="hljs-comment">// No problem, another immutable reference</span>
    <span class="hljs-keyword">let</span> r3 = &amp;<span class="hljs-keyword">mut</span> s; <span class="hljs-comment">// BIG PROBLEM, can't have mutable reference here</span>
    <span class="hljs-comment">// cannot borrow `s` as mutable because it is also borrowed as immutable</span>

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}, {}, and {}"</span>, r1, r2, r3);
}
</code></pre>
<p>The error you see when compiling this code is because Rust doesn't allow a mutable reference (<code>r3</code>) to coexist with immutable references (<code>r1</code> and <code>r2</code>) to the same data. This rule ensures that when you're reading data immutably, you can trust that it won't change.</p>
<p>Now, even though you can't have both mutable and immutable references that overlap in scope, you can have them sequentially, where the use of immutable references finish before the mutable reference begins.</p>
<p>Example:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> s = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"hello"</span>);

    <span class="hljs-keyword">let</span> r1 = &amp;s;          <span class="hljs-comment">// No problem, immutable reference 1</span>
    <span class="hljs-keyword">let</span> r2 = &amp;s;          <span class="hljs-comment">// No problem, immutable reference 2</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{} and {}"</span>, r1, r2);
    <span class="hljs-comment">// r1 and r2 are no longer used after this point</span>

    <span class="hljs-keyword">let</span> r3 = &amp;<span class="hljs-keyword">mut</span> s;      <span class="hljs-comment">// No problem, mutable reference</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, r3);
}
</code></pre>
<p>In this code, we first create two immutable references, <code>r1</code> and <code>r2</code>, to the string <code>s</code>. We use them in the <code>println!</code> statement and then don't use them anymore. After that, we introduce a mutable reference <code>r3</code> to the same string <code>s</code>. This works because the immutable references are no longer in use when the mutable reference is introduced.</p>
<p>This separation in time ensures that, at any given moment, there is only one type of reference (mutable or immutable) that has access to the data.</p>
<h2 id="heading-dangling-references">Dangling References</h2>
<p>A dangling reference occurs when a reference still exists but points to memory that has been deallocated or is otherwise invalid.</p>
<p>Example:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> reference_to_string: &amp;<span class="hljs-built_in">String</span>;

    {
        <span class="hljs-comment">// s is a new String</span>
        <span class="hljs-keyword">let</span> s = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"hello"</span>);
        <span class="hljs-comment">// assigning reference of String `s`</span>
        reference_to_string = &amp;s; 
    }<span class="hljs-comment">// Here, s goes out of scope, and is dropped. Its memory goes away.</span>

    <span class="hljs-comment">// ERROR: `s` is no longer available, leaving `reference_to_string` dangling</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, reference_to_string);
    <span class="hljs-comment">// Oops! `reference_to_string` now points to invalid memory</span>
}
</code></pre>
<p>In this example, a reference <code>reference_to_string</code> is created inside a block, pointing to a <code>String</code> created within the same block. However, once the block ends, the <code>String</code> (<code>s</code>) is deallocated, leaving <code>reference_to_string</code> with a dangling reference. Attempting to use <code>reference_to_string</code> after the referenced data has been dropped results in undefined behavior and is typically caught by the Rust compiler.</p>
<h3 id="heading-handling-dangling-references">Handling Dangling References</h3>
<p>Rust's borrow checker and ownership system are designed to prevent dangling references, which occur when a reference points to invalid or deallocated memory. Rust's strict rules and compile-time checks help eliminate the risk of dangling references, ensuring program reliability and memory safety.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>References and borrowing are powerful features of Rust that enable safe and efficient memory management. By understanding the types of references, their rules, and best practices for usage, Rust developers can write robust and reliable code with confidence. Rust's ownership system, coupled with its borrow checker, provides a unique approach to memory safety, making it a language of choice for building high-performance and secure software.</p>
<p>By mastering ownership, references and borrowing in Rust, developers can harness the full potential of the language's memory management capabilities, creating software that is not only efficient but also resilient to common memory-related issues.</p>
]]></content:encoded></item><item><title><![CDATA[Mastering Memory Management in Rust: Ownership and Variable Scope]]></title><description><![CDATA[Rust is well known for its memory safety and efficiency, largely due to its unique ownership system. In Rust, memory management revolves around ownership rules, which prevent common issues like memory leaks and data races. This article dives into the...]]></description><link>https://blog.iamdipankarpaul.com/mastering-memory-management-in-rust-ownership-and-variable-scope</link><guid isPermaLink="true">https://blog.iamdipankarpaul.com/mastering-memory-management-in-rust-ownership-and-variable-scope</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[beginner]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Dipankar Paul]]></dc:creator><pubDate>Thu, 31 Oct 2024 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750248157931/f05cb13c-831e-4ad2-99c8-0b2d772edc34.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Rust is well known for its memory safety and efficiency, largely due to its unique ownership system. In Rust, memory management revolves around ownership rules, which prevent common issues like memory leaks and data races. This article dives into the core concepts of ownership in Rust, exploring variable scope, ownership rules, data movement, and ownership in functions with clear examples.</p>
<h2 id="heading-variable-scope">Variable Scope</h2>
<p>A variable's scope in Rust defines where it is valid within the code. In Rust, variables are only accessible within the scope they're defined in. Once the scope ends, the variable goes out of scope, and its memory is automatically freed.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Example of variable scope</span>
{
    <span class="hljs-keyword">let</span> name = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"hello rust!"</span>);
    <span class="hljs-comment">// 'name' is valid within this code block only</span>
}
<span class="hljs-comment">// 'name' is no longer valid here</span>
</code></pre>
<p>Here the variable <code>name</code> is only available inside the code block, i.e., between the curly braces <code>{}</code>. We cannot use the <code>name</code> variable outside the closing curly brace.</p>
<h2 id="heading-ownership-rules">Ownership Rules</h2>
<p>Rust enforces ownership rules to ensure memory safety:</p>
<ol>
<li><p>Each value has a single owner.</p>
</li>
<li><p>There can only be one owner at a time.</p>
</li>
<li><p>When the owner goes out of scope, the value is dropped.</p>
</li>
</ol>
<h2 id="heading-data-move">Data Move</h2>
<p>When a value is assigned to another variable, ownership moves, preventing multiple ownership and ensuring memory safety.</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-comment">// rule no. 1 </span>
    <span class="hljs-comment">// String value has an owner variable `fruit1`</span>
    <span class="hljs-keyword">let</span> fruit1 = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Banana"</span>);

    <span class="hljs-comment">// rule no. 2</span>
    <span class="hljs-comment">// only one owner at a time</span>
    <span class="hljs-comment">// ownership moves to another variable `fruit2`</span>
    <span class="hljs-keyword">let</span> fruit2 = fruit1;

    <span class="hljs-comment">// rule no. 3</span>
    <span class="hljs-comment">// error, out of scope, value is dropped</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"fruit1 = {}"</span>, fruit1);
    <span class="hljs-comment">// cannot print variable fruit1 because ownership has moved</span>

    <span class="hljs-comment">// print value of fruit2 on the screen</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"fruit2 = {}"</span>, fruit2); <span class="hljs-comment">// prints → Banana</span>
}
</code></pre>
<p>Here, <code>fruit1</code> was the owner of the String <code>Banana</code>.</p>
<p>A <code>String</code> type stores data both on the <code>stack</code> and the <code>heap</code>. It means that when we bind a <code>String</code> to a variable <code>fruit1</code>, the memory representation looks like this:</p>
<p><strong>Stack(fruit1):</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Name</td><td>Value</td></tr>
</thead>
<tbody>
<tr>
<td>ptr</td><td>--&gt;</td></tr>
<tr>
<td>length</td><td>6</td></tr>
<tr>
<td>capacity</td><td>6</td></tr>
</tbody>
</table>
</div><p><strong>Heap:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Index</td><td>Value</td></tr>
</thead>
<tbody>
<tr>
<td>0</td><td>B</td></tr>
<tr>
<td>1</td><td>a</td></tr>
<tr>
<td>2</td><td>n</td></tr>
<tr>
<td>3</td><td>a</td></tr>
<tr>
<td>4</td><td>n</td></tr>
<tr>
<td>5</td><td>a</td></tr>
</tbody>
</table>
</div><p>A <code>String</code> type holds a pointer that points to the memory that holds the content of the string. The length and capacity of the string are stored in the stack.</p>
<p>Now, when we assign <code>fruit1</code> to <code>fruit2</code>, this is how the memory representation looks like:</p>
<p><strong>Stack(fruit2):</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Name</td><td>Value</td></tr>
</thead>
<tbody>
<tr>
<td>ptr</td><td>--&gt;</td></tr>
<tr>
<td>length</td><td>6</td></tr>
<tr>
<td>capacity</td><td>6</td></tr>
</tbody>
</table>
</div><p>Rust will invalidate (drop) the first variable <code>fruit1</code>, and move the value to another variable <code>fruit2</code>. This way two variables cannot point to the same content. <strong>Rule no. 2, there can only be one owner of the value at a time.</strong></p>
<blockquote>
<p><strong>Note:</strong> The above concept is applicable for data types that don't have fixed sizes in memory and use the heap memory to store the contents.</p>
</blockquote>
<h2 id="heading-data-copy">Data Copy</h2>
<p>Primitive types like integers and floats, which have a fixed size, are copied instead of moved when assigned to another variable.</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> x = <span class="hljs-number">11</span>;

    <span class="hljs-comment">// copies data from x to y</span>
    <span class="hljs-comment">// ownership rules are not applied here </span>
    <span class="hljs-keyword">let</span> y = x;

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"x = {x}, y = {y}"</span>); <span class="hljs-comment">// prints x = 11, y = 11</span>
}
</code></pre>
<p>Here, <code>x</code> variable can be used afterward, unlike a <code>move</code> without worrying about ownership, even though <code>x</code> is assigned to <code>y</code>.</p>
<p>This copying is possible because of the <code>Copy</code> trait available in primitive types in Rust. When we assign <code>x</code> to <code>y</code>, a copy of the data is made.</p>
<blockquote>
<p><strong>Note:</strong> A <code>trait</code> is a way to define shared behavior in Rust. We will discuss the traits later.</p>
</blockquote>
<h2 id="heading-ownership-in-functions">Ownership in Functions</h2>
<p>When passing variables to functions, ownership can move or be copied, depending on the type of variable.</p>
<ul>
<li><p>Stack allocated types will copy the data when passed into a function.</p>
</li>
<li><p>Heap allocated data types will move the ownership of the variable to the function.</p>
</li>
</ul>
<p><strong>Example: Passing String to a function</strong></p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">print_fruit</span></span>(<span class="hljs-built_in">str</span>: <span class="hljs-built_in">String</span>) {
    <span class="hljs-comment">// str comes into scope</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"str = {}"</span>, <span class="hljs-built_in">str</span>);
} <span class="hljs-comment">// str goes out of scope and is dropped, plus memory is freed</span>

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-comment">// fruit comes into scope</span>
    <span class="hljs-keyword">let</span> fruit = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"apple"</span>);

    <span class="hljs-comment">// ownership of fruit moves into the function</span>
    print_fruit(fruit); <span class="hljs-comment">// value moved here</span>
    <span class="hljs-comment">// prints → apple</span>
    <span class="hljs-comment">// fruit is moved to the function so is no longer available here</span>

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"fruit = {}"</span>, fruit); <span class="hljs-comment">// error</span>
}
</code></pre>
<p>Here, the value of the <code>fruit</code> variable is moved into the function <code>print_fruit()</code> because <code>String</code> type uses heap memory.</p>
<p><strong>Example: Passing Integer to a function</strong></p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">print_number</span></span>(value: <span class="hljs-built_in">i32</span>) {
    <span class="hljs-comment">// value comes into scope</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"value = {}"</span>, value);
} <span class="hljs-comment">// value goes out of scope and is dropped, plus memory is freed</span>

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-comment">// number comes into scope</span>
    <span class="hljs-keyword">let</span> number = <span class="hljs-number">10</span>;

    <span class="hljs-comment">// value of the number is copied into the function</span>
    print_number(number);

    <span class="hljs-comment">// number variable can be used here</span>
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"number = {}"</span>, number);
}
</code></pre>
<p>Here, the value of the <code>number</code> variable is copied into the function <code>print_number()</code> because the <code>i32</code> (integer) type uses stack memory.</p>
<h2 id="heading-ownership-table">Ownership table</h2>
<p>A table summarizing types that implement the <code>Copy</code> trait and types that are typically heap-allocated.</p>
<ul>
<li><p>Types that implement <code>Copy</code> trait have their values copied when assigned to another variable, preventing ownership transfer.</p>
</li>
<li><p>Heap-allocated types involve ownership transfer because they are dynamically allocated on the heap, and ownership needs to be managed properly.</p>
</li>
<li><p>Whether a compound or custom type implements <code>Copy</code> depends on its internal structure.</p>
</li>
</ul>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Type</td><td>Implements <code>Copy</code></td><td>Heap-Allocated</td><td>Ownership Transfer</td></tr>
</thead>
<tbody>
<tr>
<td>Integer Types</td><td>Yes</td><td>No</td><td>No</td></tr>
<tr>
<td>Floating-Point Types</td><td>Yes</td><td>No</td><td>No</td></tr>
<tr>
<td>Character Type</td><td>Yes</td><td>No</td><td>No</td></tr>
<tr>
<td>Boolean Type</td><td>Yes</td><td>No</td><td>No</td></tr>
<tr>
<td>Tuple (if elements are <code>Copy</code>)</td><td>Yes</td><td>No</td><td>No</td></tr>
<tr>
<td>Array (if elements are <code>Copy</code>)</td><td>Yes</td><td>No</td><td>No</td></tr>
<tr>
<td>Structs (if fields are <code>Copy</code>)</td><td>Yes</td><td>No</td><td>No</td></tr>
<tr>
<td>String</td><td>No</td><td>Yes</td><td>Yes</td></tr>
<tr>
<td>Vec</td><td>No</td><td>Yes</td><td>Yes</td></tr>
<tr>
<td>HashMap, HashSet</td><td>No</td><td>Yes</td><td>Yes</td></tr>
<tr>
<td>Custom Types</td><td>Depends</td><td>Depends</td><td>Depends</td></tr>
</tbody>
</table>
</div><h2 id="heading-conclusion">Conclusion</h2>
<p>Understanding ownership in Rust is crucial for writing safe and efficient code. By following ownership rules and mastering variable scope, developers can leverage Rust's memory management features to create robust and reliable software. Rust's ownership system, coupled with its powerful type system, ensures that memory-related errors are caught at compile time, resulting in more reliable software with fewer runtime surprises.</p>
]]></content:encoded></item></channel></rss>