A masterful demonstration of how fundamental physics can breathe life into the rigid constraints of ASCII art. It seamlessly blends retro aesthetics with modern interactive performance.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
I Turned My Logo Into 10,000 Scrambling ASCII Art (and Made It React to Your Mouse Movement)Added:
If you've been following the channel recently, you might have noticed I've been spending a lot of time exploring ASCII aesthetics. It's something that's been showing up across a lot of creative sites lately, and honestly, I've been really enjoying digging into it. After covering the image reveal animation in the last video, I stumbled across this site last weekend. It's called Artifact, and right on the landing page, they have this really cool interactive ASCII logo.
As you can see, the entire logo is rendered out of ASCII characters across a grid, and when you move your cursor over it, the characters physically push away and spring back into place. Really satisfying to interact with, and it's one of those effects that looks deceptively simple, but has quite a bit going underneath. Since I'd already been deep into ASCII territory at that point, I found the concept really compelling [music] and thought it was worth exploring further. So, after a bit of experimentation, I put together this interactive ASCII logo animation in vanilla [music] JavaScript. What's happening under the hood is the logo gets sampled down into grid of cells.
Each cell's brightness maps to an ASCII character, and then a physics system runs on top of that. So, when your cursor gets close, the characters push away and spring back using velocity and damping. There is also a separate interval constantly randomizing the characters on all the cells, which gives it that flickering look. In this video, I'll walk you through how to build the whole thing from scratch, just HTML, CSS, and [music] JavaScript. No libraries needed. If you'd like to access the source code for this project along with hundreds of other similar micro-projects and a brand new website template every month, you can check out the pro membership via the link in the description. And if you find my work helpful, make sure you leave a like on the video and subscribe to the channel if you haven't already. All right, let's get into the code.
First, I'll create a section with the class of hero. This is going to be our main container that holds everything together. Next, I'll add a canvas element with the ID of grid. This is where the entire effect [music] gets drawn. Every cell, every character, all of it lives on this canvas. Then I'll add a div with the class of logo, and inside it, an image tag with the ID of source. This is the logo we want to render as ASCII. It won't actually be visible on screen, it's just there so we have something to sample pixel data from. And that's pretty much it for the markup. Let's get to the styling.
First, I'll add a basic reset, zeroing out the margin, padding, and setting box sizing to border box across everything.
Then we have the hero section. I'll give it a relative position, full width, full viewport height, and a dark background color. Next, on the canvas, I'll set it to display block and give it full width and height, so it fills the entire hero section. That's all it needs. Then for the logo div, I'll absolutely position it and center it using the translate trick. I'll also give it a width of 75% so it sits nicely within the viewport without going edge to edge. And finally, the image inside the logo, full width, full height, object fit contain to keep the proportions intact and visibility hidden. We don't want this image to actually show up, it's purely there for sampling. The canvas is what the user sees. All right, let's get into the JavaScript.
Now, before we get into the JavaScript, just a quick note. This is going to be a walk-through of the overall approach and how everything fits together, rather than a line-by-line explanation of every single concept. If you've watched the channel for a while, you already know the drill. The focus here is on the technique, the thinking behind it, and how to actually build these kinds of effects. With that said, let's get into it. I'll start by defining all the configuration constants at the top. We have the cell size and cell gap, and from those two, I'll derive cell step, which is just the two added together.
That's the value we'll use for spacing everything out consistently. Then we have the grid color and the character color, the ASCII character string, a brightness threshold, and the physics values, push radius, push force, spring, and damping. We'll get to what those do a little later. Having all of this at the top means you can tweak the entire effect just by changing these values.
Then I'll grab the canvas element by its ID, get the 2D context with alpha enabled, read the device pixel ratio for sharpness on high DPI screens, and grab the logo image by its ID. Next, I'll declare columns, rows, and empty row cells array at the module level. These need to be accessible across multiple functions. Then I'll write the setup canvas function. This is where we handle sizing everything correctly. First, it checks the screen width and adjusts the cell size and gap for mobile, so smaller screens get a tighter grid. Then we calculate how many columns and rows we can fit, set the canvas dimensions accounting for the device pixel ratio, and apply scale transform so everything draws at the right size.
>> [music] >> Next, the draw grid function. This clears the canvas and then loops through every row and column, painting a small rectangle for each cell in the grid color. That's what gives us that baseline dot grid look before any character are drawn. [music] Then I'll call setup canvas and draw grid straight away. This gets the grid up on screen immediately on load. Now, the most important function, sample logo into cells. This is where we figure out which cells should show a character and which ones should stay as empty grid. First, I'll read the logo image's bounding rect to find out exactly where it sits on screen >> [music] >> and how large it is in terms of grid cells. Then I'll create a small off-screen canvas, just the size of the logo in grid units, draw the image onto it at that tiny resolution, and read the pixel data back. That's how we sample it. Then we loop through every cell in the full grid, and for cells that fall within the logo's bounds, we calculate the brightness of that pixel using a standard luminance formula. If the brightness is above our threshold, the cell is lit, and the brightness value maps to a character in our ASCII string.
Light pixels get dots, heavy pixels get characters like hash and at the rate.
Every cell, lit or not, gets pushed into the cells array with its position, its character, and is lit flag, and offset and velocity values initialized to zero.
Those last four are what physics system will use later. Next, we'll look at how we actually render all of this onto the canvas. Now, the render frame function.
This is what actually draws everything to screen on every frame. [music] I'll set the font, text baseline, and text align once at the top, then clear the canvas. First pass, we draw all the grid cells as filled rectangles in the grid color, every single cell, lit or not. Second pass, we loop through again, but this time we skip anything that isn't lit. And for the ones that are, we calculate the draw position using the cell's column and row plus its current offset values, and draw the character there. Those offsets are what allow the characters to physically move away from their original position. Then I'll write the init function. This just calls setup canvas, sample logo into cells, and render frame in order. Clean entry point that sets everything up from scratch.
Next, I'll attach init to the resize event so the grid recalculates correctly when the window size changes, and then either call init immediately if the image is already loaded, or wait for the load event. Then I'll set up a set interval running every 50 milliseconds.
On every tick, we loop through all lit cells and assign each one a random character from the ASCII string, then re-render. That's what gives us that constant flickering feel across the logo. Next, I'll set up the mouse state, a column and row initialized way off screen, and an is moving flag set to false. Now, the update physics function.
This is where the interactive feel comes from. We loop through every lit cell and skip the ones that aren't. If the mouse is moving, we calculate the distance between the cell's current position and the mouse position in grid units. If that distance falls within our push radius, we apply force pushing the cell away, stronger the closer it is, using a squared falloff. Then regardless of whether the mouse is near, we apply spring force pulling the cell back toward its original position. Multiply the velocity by the damping value to bleed off energy over time, and add the velocity to the offset. The small threshold checks at the end just snap the cell to rest once it's essentially stopped moving, so we are not running tiny unnecessary calculations forever.
Then the animation loop. This calls update physics and render frame on every frame using request animation frame, keeping everything running smoothly.
Finally, I'll add the mouse move listener. This converts the raw pixel position of the cursor into the grid units and sets is moving to true. Then a short idle timer resets is moving to false if the mouse stops, so the spring physics can settle everything back into place. And the mouse leave listener pushes the mouse position way off screen and sets is moving to false so nothing gets pushed when the cursor leaves the window. Then I'll kick off the animation loop, and that's it. So, that's the full ASCII logo effect, a sampled image, a flickering character grid, and a physics system that makes it actually react to you. Hope you found the video helpful.
See you in the next one.
Related Videos
Agentforce NOW AMA: Build with React and Salesforce Multi-Framework
SalesforceDevs
490 views•2026-05-28
How agent o11y differs from traditional o11y — Phil Hetzel, Braintrust
aiDotEngineer
450 views•2026-05-28
WEB TECHNOLOGIES UNIT-2 | Degree 4th sem BCOM Computers web technologies unit-2 full explanation💯✅
LearnwithSahera
1K views•2026-05-29
More tests are always better? How to use AI to identify tests that bring little value
Alliance4Qualification
335 views•2026-05-29
Search Algorithms Explained in 60 Seconds! 🤖💨
samarthtuliofficial
218 views•2026-06-01
People of Game of Thrones using JavaScript DOM
AltCampus
296 views•2026-05-30
Introduction to Problem Solving Part - 1 | Lecture 1 | Intermediate DSA
ascensionix
107 views•2026-05-29
🚀 BCS613C Compiler Design | Module 1 to 5 Schema Evaluation 🔥 | VTU 6th Sem 💯 #VTU #bcs613c #exam
Pranavaa-y4y
104 views•2026-06-02











