- Venture The Unknown
- Posts
- Exploring Infinite Canvas: My Journey & Insights
Exploring Infinite Canvas: My Journey & Insights
Let me show you how you can make infinite canvas with only 2 HTML elements
Today I’m exploring infinite canvas. Here’s a digest of what you’ll get after reading this post:
An infinite canvas of your own
My take on how to reverse-engineer some of web apps’ cool features
Background
I’ve been fascinated by design tools like framer and figma for the canvas (not canvas
tag in HTML) that the user is able to interact with and edit objects (frames, images, shapes, text) inside that canvas. Today, we’re going to explore Framer’s infinite canvas.

The UI of Framer showcases an editable canvas in the center.
In this canvas, you can perform wheel gestures and move the frames inside the canvas with your mouse. This type of canvas is usually called an infinite canvas
A couple months back, I was working on Typedream (acq by Beehiiv) which is a website builder similar to Framer. However, we did not have this functionality early on and postponed the development of this for a while.
Now, I have more time to re-focus building the website builder on Beehiiv and decided to give it a shot. Most of these things were unknown to me just a few weeks back, but let’s dive into the unknown!
How to figure out the inner working of web apps
The first step is to find out how things are currently implemented. Here’s how I dive deep into how Framer did theirs.
Step 1: Play with the product
Strictly focus on the product you want to replicate. Figure out what it can or can’t do. Revisit this step often because you will find new observations over time.
Step 2: Inspect & find HTML element
All things on the web consist of HTML, JavaScript, and CSS. No matter the framework, it all boils down to these three core elements. I usually inspect the final result (HTML, CSS) and try to reverse-engineer the JavaScript.
Open your inspect elements tab, go to the elements tab, and find the specific HTML element you are interested in. In my case, it’s the moving canvas.

Finding HTML element that changes when we move objects in canvas
Step 3: Reverse engineer
I ask myself: “How can I achieve this HTML & CSS behavior using <insert-framework>?”
Frameworks can be core web app libraries like remix
, next.js
, react
and/or it can be the more specific gesture or dnd libraries like use-gesture/react
, react-dnd
, etc.
My take on how it is implemented
The core to this infinite canvas behavior is the combination of CSS translate
and event listeners (MouseEvent
or KeyboardEvent
)
Step 1: Setup the elements
For demonstration purposes, you really only need 2 element:
The canvas window, referred as
main
with id:infinite-canvas
The canvas object, referred as
div
with id:object-to-move
Here’s the code snippet:
// This canvas will be scrollable
<main
id="infinite-canvas"
style={{ width: "100dvw", height: "100dvh", overscrollBehavior: "none" }}
>
{/* Second: setup object to move */}
<div
id="object-to-move"
style={{
position: "absolute",
backgroundColor: "red",
height: "300px",
width: "300px",
placeContent: "center",
}}
>
<p style={{ textAlign: "center" }}>Scroll to move or pinch to zoom</p>
</div>
</main>
Step 2: Add in necessary CSS for movement of main canvas frame
Similar to how you have a single frame you can edit in Framer, you attach a little bit of CSS combined with some Javascript
// This canvas will be scrollable
<main
...
>
<div
id="object-to-move"
style={{
... the rest of the styles
transform: `scale(${scale}) translate(${x}px, ${y}px)`, // 👈 Add this line!
}}
>
...
</div>
</main>
Note that scale
, x
,and y
are variables that we haven’t defined yet.
Step 3: Define event listeners & handlers
We now need to know what events to listen to. There are 2 events I care about:
When user scroll using touch pad
When user pinch (for zooming in or zooming out)
You can add more event listener and keyboard shortcut as you like to modify how the infinite canvas to react on user input.
I’m going to cheat a little bit here and use a library called @use-gesture/react
(docs) to help me with attaching listeners I need
Here’s the setup:
const gestureRef = useRef<HTMLElement>(null);
const [x, setX] = useState(0);
const [y, setY] = useState(0);
const [scale, setScale] = useState(1);
useGesture(
{
// Here's where I define event listener
// for scroll behavior
onWheel: (event) => {
// requestAnimationFrame for performance!
requestAnimationFrame(() => {
// We're setting the x & y transform
// based on the distance user has
// travelled using their trackpad/scrollwheel
setX((prev) => prev - event.delta[0]);
setY((prev) => prev - event.delta[1]);
});
},
// Define pinch event listener here
onPinch: ({ offset }) => {
requestAnimationFrame(() => {
// Zoom in or out based on the offset size
// try console logging the values provided by
// the listener!
setScale(offset[0]);
});
},
},
{
// TODO: where should we attach the handlers?
target: gestureRef,
},
);
I’m using react to render my component. You can do it with vanilla javascript, but I’m just so used to react ecosystem so I’m going to use them for simplicity.
Step 4: Attach listeners/handlers
Final step is to attach the event listeners.
// This canvas will be scrollable
<main
ref={gestureRef} // 👈 Add the gestureRef here
id="infinite-canvas"
style={{ width: "100dvw", height: "100dvh", overscrollBehavior: "none" }}
>
Final Result
Here’s the final look. It’s far from being as polished as Framer’s. But It does the job.

Hope you learned a thing or two. Cheers 🍻