Create Interactive Pill Animation Using Matter.js
A Guide To Building Awwwards Worthy Website Animations

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 visually stunning digital experience. Among the many animations, our team's favorite was the Interactive Pill Animation.

Let's dive deep into the technicalities behind making this draggable pill-interaction animation using Matter.js and Next.js
Breaking Down The Animation
Pills dropping from above when the section comes into view.
Interacting and moving the pills around using cursor.
The animation is created using a 2D physics engine for the web called Matter.js
// yarn
yarn add matter-js
//npm
npm i matter-js
If you are using typescript, make sure you add the types for Matter.js
// yarn
yarn add -D @types/matter-js
//npm
npm i -D @types/matter-js
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'.
const matterRef = React.createRef<HTMLDivElement>();
Create a wrapper for the scene
<div ref={matterRef}></div>
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
const canvas = matterRef.current;
let Engine = Matter.Engine,
Render = Matter.Render,
Runner = Matter.Runner,
Bodies = Matter.Bodies,
Composite = Matter.Composite,
Mouse = Matter.Mouse,
MouseConstraint = Matter.MouseConstraint;
Events;
This will create a Canvas for us to work with.
We need a way to render objects into our canvas, that is where the Matter.js Engine comes into the picture.
Engine is nothing but a function provided by the Matter.js to paint object into our canvas.
We can create a Engine using the following Code.
// create an engine
let engine = Engine.create();
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.
let render = Render.create({
element: canvas || undefined,
engine: engine,
options: {
wireframes: false,
width: window.innerWidth,
height: window.innerHeight / 3,
background: "transparent",
},
});
In Many iOS device rendered matter bodies are pixelated. To fix that, Matter.js lets us set the PixelRatio.
Render.setPixelRatio(render, window.devicePixelRatio as number);
Adding the above line will fix all the pixelation problems. Learn more about this here.
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 sprites can be used.
To create your own sprite first define your body which can be done as follows.
Select a predefined body from this list

Each body has 4 parameters
Matter.Bodies.circle(x, y, radius, [options], [maxSides]) β [Body]Each body has option in which you can select the fillStyle, strokeStyles and other properties
const boxA = Bodies.circle(400, 300, 80, { render: { fillStyle: '#F35e66', strokeStyle: 'black', lineWidth: 1, }, });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
const pillData:{ name: string; url: string; height: number; width: number; }[]
To render the pills, paste the following code.
const renderPills = pillData.map((pill) => {
return Bodies.rectangle(
Math.random() * window.innerWidth,
50,
pill.width,
pill.height,
{
render: {
fillStyle: "transparent",
sprite: {
xScale: 1,
yScale: 1,
texture: pill.url,
},
},
}
);
});
Inside the render option you can add a method called as sprite, where you can add the texture of your asset stored as url.
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 Composite Module.
Composite.add(engine.world, [
...renderPills,
]);
After this run the renderer by calling the Renderer method
Runner.run(runner, engine);
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.
To add boundaries we will create 4 bodies which will surround our canvas: Ground, roof, wallLeft & wallRight
var ground = Bodies.rectangle(
window.innerWidth / 2,
window.innerHeight / 3,
window.innerWidth + 320,
60,
{ render: { fillStyle: "transparent" }, isStatic: true }
);
var wallLeft = Bodies.rectangle(
-80,
window.innerHeight / 2,
160,
window.innerHeight,
{ render: { fillStyle: "transparent" }, isStatic: true }
);
var wallRight = Bodies.rectangle(
window.innerWidth + 80,
window.innerHeight / 2,
160,
1200,
{ render: { fillStyle: "transparent" }, isStatic: true }
);
var roof = Bodies.rectangle(
window.innerWidth / 2 + 160,
-80,
window.innerWidth + 320,
160,
{ render: { fillStyle: "transparent" }, isStatic: true }
);
Make sure to add isStatic:true otherwise the walls will also move.
All the pills are falling from top and are bounded by walls on 4 sides. But itβs not interactive right now.
The last step is to add interactivity. For that we need 2 modules from Matter.js
Matter.MouseMatter.MouseConstraint
let mouse = Mouse.create(render.canvas);
let mouseConstraint = MouseConstraint.create(engine, {
mouse: mouse,
constraint: {
stiffness: 0.2,
render: {
visible: false,
},
},
});
//removing the mousewheel event to stop canvas from scrolling
//(works exactly like CSS propery: overflow-hidden)
mouseConstraint.mouse.element.removeEventListener(
"mousewheel",
mouseConstraint.mouse.mousewheel
);
mouseConstraint.mouse.element.removeEventListener(
"DOMMouseScroll",
mouseConstraint.mouse.mousewheel
);
//finally adding the mouse to the composite
Composite.add(engine.world, mouseConstraint);
That's it! Your interactive pill animation is ready.

Integrate this animation into your next website and take the user experience to a next level.
Leave a like if you find this valuable.
A blog by Team createxp

