<?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[createxp]]></title><description><![CDATA[A blog by Team createxp

A team of designers & developers that love to create digital experiences that make you go 'wow'!]]></description><link>https://blog.createxp.in</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1709111132784/fdfab2d1-ccef-4dae-8555-8edf50652878.png</url><title>createxp</title><link>https://blog.createxp.in</link></image><generator>RSS for Node</generator><lastBuildDate>Mon, 11 May 2026 01:57:31 GMT</lastBuildDate><atom:link href="https://blog.createxp.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Create Interactive Pill Animation Using Matter.js]]></title><description><![CDATA[Recently, our team at createxp undertook a fascinating challenge: developing a portfolio website for a creative agency. The project involved numerous animations and interactions with the primary objective of maximizing user delight and creating a vis...]]></description><link>https://blog.createxp.in/create-interactive-pill-animation-using-matterjs</link><guid isPermaLink="true">https://blog.createxp.in/create-interactive-pill-animation-using-matterjs</guid><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><category><![CDATA[CSS Animation]]></category><dc:creator><![CDATA[createxp HQ]]></dc:creator><pubDate>Sat, 03 Feb 2024 17:14:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1706977438621/ccc7ca6c-6198-49b6-bc3f-60069bbf3e53.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently, our team at <a target="_blank" href="http://www.createxp.in">createxp</a> undertook a fascinating challenge: developing a portfolio website for a creative agency. The project involved numerous animations and interactions with the primary objective of maximizing user delight and creating a visually stunning digital experience. Among the many animations, our team's favorite was the Interactive Pill Animation.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706964294731/14f2135f-d671-416a-8098-0212e6550fe5.gif" alt class="image--center mx-auto" /></p>
<p>Let's dive deep into the technicalities behind making this draggable pill-interaction animation using <strong>Matter.js</strong> and <strong>Next.js</strong></p>
<h3 id="heading-breaking-down-the-animation">Breaking Down The Animation</h3>
<ol>
<li><p>Pills dropping from above when the section comes into view.</p>
</li>
<li><p>Interacting and moving the pills around using cursor.</p>
</li>
</ol>
<p>The animation is created using a 2D physics engine for the web called <a target="_blank" href="https://brm.io/matter-js/">Matter.js</a></p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">👉</div>
<div data-node-type="callout-text">Start by adding Matter.JS</div>
</div>

<pre><code class="lang-typescript"><span class="hljs-comment">// yarn</span>

yarn add matter-js

<span class="hljs-comment">//npm</span>

npm i matter-js
</code></pre>
<p>If you are using typescript, make sure you add the types for Matter.js</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// yarn</span>

yarn add -D <span class="hljs-meta">@types</span>/matter-js

<span class="hljs-comment">//npm</span>

npm i -D <span class="hljs-meta">@types</span>/matter-js
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">👉</div>
<div data-node-type="callout-text">Create a scene</div>
</div>

<p>A scene is like a canvas where Matter.js shows its objects. To link a div to Matter.js, we make a div and give it a 'ref'.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> matterRef = React.createRef&lt;HTMLDivElement&gt;();
</code></pre>
<p>Create a wrapper for the scene</p>
<pre><code class="lang-javascript">&lt;div ref={matterRef}&gt;&lt;/div&gt;
</code></pre>
<p>In order to render objects to a scene, we need a canvas. We don’t have to create a canvas on our own, we can use the Matter.js create function to do that</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> canvas = matterRef.current;
        <span class="hljs-keyword">let</span> Engine = Matter.Engine,
            Render = Matter.Render,
            Runner = Matter.Runner,
            Bodies = Matter.Bodies,
            Composite = Matter.Composite,
            Mouse = Matter.Mouse,
            MouseConstraint = Matter.MouseConstraint;
Events;
</code></pre>
<p>This will create a Canvas for us to work with.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">👉</div>
<div data-node-type="callout-text">Activating Matter.js Engine</div>
</div>

<p>We need a way to render objects into our canvas, that is where the Matter.js Engine comes into the picture.</p>
<p>Engine is nothing but a function provided by the Matter.js to paint object into our canvas.</p>
<p>We can create a Engine using the following Code.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// create an engine</span>
<span class="hljs-keyword">let</span> engine = Engine.create();
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">👉</div>
<div data-node-type="callout-text">Render Objects</div>
</div>

<p>To render all the shapes which we want, we need to render them on the canvas. For that we can use the Matter.js inbuilt renderer. To invoke, copy the following code.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> render = Render.create({
            element: canvas || <span class="hljs-literal">undefined</span>,
            engine: engine,

            options: {
                wireframes: <span class="hljs-literal">false</span>,
                width: <span class="hljs-built_in">window</span>.innerWidth,
                height: <span class="hljs-built_in">window</span>.innerHeight / <span class="hljs-number">3</span>,
                background: <span class="hljs-string">"transparent"</span>,
            },
});
</code></pre>
<p>In Many iOS device rendered matter bodies are pixelated. To fix that, Matter.js lets us set the PixelRatio.</p>
<pre><code class="lang-typescript">Render.setPixelRatio(render, <span class="hljs-built_in">window</span>.devicePixelRatio <span class="hljs-keyword">as</span> <span class="hljs-built_in">number</span>);
</code></pre>
<p>Adding the above line will fix all the pixelation problems. Learn more about this <a target="_blank" href="https://brm.io/matter-js/docs/classes/Render.html#method_setPixelRatio">here</a>.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Create Sprites</div>
</div>

<p>Matter.js treats each element as rigid body. It provides a lot of pre defined bodies which you can use in your projects. But in our case we wanted our own images to be rendered for that <code>sprites</code> can be used.</p>
<p>To create your own sprite first define your body which can be done as follows.</p>
<ul>
<li><p>Select a predefined body from this <a target="_blank" href="https://brm.io/matter-js/docs/classes/Bodies.html">list</a></p>
</li>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706965920888/b4f2bf7c-a3f3-4a32-97ae-eda5b2971bec.png" alt /></p>
</li>
<li><p>Each body has 4 parameters</p>
<p>  <code>Matter.Bodies.circle(x, y, radius, [options], [maxSides]) → [Body]</code></p>
</li>
<li><p>Each body has option in which you can select the fillStyle, strokeStyles and other properties</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">const</span> boxA = Bodies.circle(<span class="hljs-number">400</span>, <span class="hljs-number">300</span>, <span class="hljs-number">80</span>, {
        render: {
        fillStyle: <span class="hljs-string">'#F35e66'</span>,
        strokeStyle: <span class="hljs-string">'black'</span>,
        lineWidth: <span class="hljs-number">1</span>,
        }, 
  });
</code></pre>
</li>
<li><p>In our case we wanted to render our own custom pills. We exported all the pill assets and defined it in a json file as shown</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">const</span> pillData:{
      name: <span class="hljs-built_in">string</span>;
      url: <span class="hljs-built_in">string</span>;
      height: <span class="hljs-built_in">number</span>;
      width: <span class="hljs-built_in">number</span>;
  }[]
</code></pre>
</li>
</ul>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Render the pills</div>
</div>

<p>To render the pills, paste the following code.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> renderPills = pillData.map(<span class="hljs-function">(<span class="hljs-params">pill</span>) =&gt;</span> {
      <span class="hljs-keyword">return</span> Bodies.rectangle(
                <span class="hljs-built_in">Math</span>.random() * <span class="hljs-built_in">window</span>.innerWidth,
                <span class="hljs-number">50</span>,
                pill.width,
                pill.height,
                {
                    render: {
                        fillStyle: <span class="hljs-string">"transparent"</span>,
                        sprite: {
                            xScale: <span class="hljs-number">1</span>,
                            yScale: <span class="hljs-number">1</span>,
                            texture: pill.url,
                        },
                    },
                }
            );
        });
</code></pre>
<p>Inside the render option you can add a method called as sprite, where you can add the texture of your asset stored as url.</p>
<p>After the pills are rendered via the engine we need a way to show it to the canvas. For that we need to add all the things to the <strong>Composite Module.</strong></p>
<pre><code class="lang-typescript">Composite.add(engine.world, [
        ...renderPills,
]);
</code></pre>
<p>After this run the renderer by calling the <strong>Renderer method</strong></p>
<pre><code class="lang-typescript">Runner.run(runner, engine);
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Add boundary to the section</div>
</div>

<p>All the pills should now be rendered but there is a problem left. There is no boundary so the pills are not bounded in a region and they fall indefinitely.</p>
<p>To add boundaries we will create 4 bodies which will surround our canvas: Ground, roof, wallLeft &amp; wallRight</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">var</span> ground = Bodies.rectangle(
            <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-number">2</span>,
            <span class="hljs-built_in">window</span>.innerHeight / <span class="hljs-number">3</span>,
            <span class="hljs-built_in">window</span>.innerWidth + <span class="hljs-number">320</span>,
            <span class="hljs-number">60</span>,
            { render: { fillStyle: <span class="hljs-string">"transparent"</span> }, isStatic: <span class="hljs-literal">true</span> }
        );
<span class="hljs-keyword">var</span> wallLeft = Bodies.rectangle(
            <span class="hljs-number">-80</span>,
            <span class="hljs-built_in">window</span>.innerHeight / <span class="hljs-number">2</span>,
            <span class="hljs-number">160</span>,
            <span class="hljs-built_in">window</span>.innerHeight,
            { render: { fillStyle: <span class="hljs-string">"transparent"</span> }, isStatic: <span class="hljs-literal">true</span> }
        );
<span class="hljs-keyword">var</span> wallRight = Bodies.rectangle(
            <span class="hljs-built_in">window</span>.innerWidth + <span class="hljs-number">80</span>,
            <span class="hljs-built_in">window</span>.innerHeight / <span class="hljs-number">2</span>,
            <span class="hljs-number">160</span>,
            <span class="hljs-number">1200</span>,
            { render: { fillStyle: <span class="hljs-string">"transparent"</span> }, isStatic: <span class="hljs-literal">true</span> }
        );
<span class="hljs-keyword">var</span> roof = Bodies.rectangle(
            <span class="hljs-built_in">window</span>.innerWidth / <span class="hljs-number">2</span> + <span class="hljs-number">160</span>,
            <span class="hljs-number">-80</span>,
            <span class="hljs-built_in">window</span>.innerWidth + <span class="hljs-number">320</span>,
            <span class="hljs-number">160</span>,
            { render: { fillStyle: <span class="hljs-string">"transparent"</span> }, isStatic: <span class="hljs-literal">true</span> }
        );
</code></pre>
<p>Make sure to add <code>isStatic:true</code> otherwise the walls will also move.</p>
<p>All the pills are falling from top and are bounded by walls on 4 sides. But it’s not interactive right now.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Add Interactivity</div>
</div>

<p>The last step is to add interactivity. For that we need 2 modules from Matter.js</p>
<ul>
<li><p><code>Matter.Mouse</code></p>
</li>
<li><p><code>Matter.MouseConstraint</code></p>
</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> mouse = Mouse.create(render.canvas);

        <span class="hljs-keyword">let</span> mouseConstraint = MouseConstraint.create(engine, {
            mouse: mouse,
            constraint: {
                stiffness: <span class="hljs-number">0.2</span>,
                render: {
                    visible: <span class="hljs-literal">false</span>,
                },
            },
        });

<span class="hljs-comment">//removing the mousewheel event to stop canvas from scrolling</span>

<span class="hljs-comment">//(works exactly like CSS propery: overflow-hidden)</span>
        mouseConstraint.mouse.element.removeEventListener(
            <span class="hljs-string">"mousewheel"</span>,
            mouseConstraint.mouse.mousewheel
        );
        mouseConstraint.mouse.element.removeEventListener(
            <span class="hljs-string">"DOMMouseScroll"</span>,
            mouseConstraint.mouse.mousewheel
        );
<span class="hljs-comment">//finally adding the mouse to the composite</span>

Composite.add(engine.world, mouseConstraint);
</code></pre>
<p>That's it! Your interactive pill animation is ready.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706976049674/7f6bb60e-dfeb-4d1e-b911-1cc318b3db28.gif" alt class="image--center mx-auto" /></p>
<p>Integrate this animation into your next website and take the user experience to a next level.</p>
<p>Leave a like if you find this valuable.</p>
<p>A blog by <strong>Team createxp</strong></p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Want help in developing high-performing websites, connect with us through our website 👉 <a target="_blank" href="http://www.createxp.in">www.createxp.in</a></div>
</div>]]></content:encoded></item></channel></rss>