Jeremy Threlfall
This project creates a smooth blurry loading effect for a webpage. As the page loads, a background image gradually becomes clearer while a visible loading percentage fades out. The blur and fade are animated progressively to give users a sense of progress. The logic increments the loading value over time and updates the visual elements accordingly, while transitions make the changes appear smooth and natural. Overall, it provides an engaging, polished loading experience with minimal and maintainable code.
With just two lines of HTML, this project is very straightforward. The section element serves as the background image, and the div displays the loading text. Together, they provide the visual foundation for the blurry loading effect.
<section class="bg"></section>
<div class="loading-text"></div>The .bg class handles the background image and the blurry effect. A linear gradient is layered over the image to slightly darken it, making the loading text more visible. Since blurring can make edges look rough, the background is slightly offset and expanded beyond the viewport so the edges stay smooth. The initial blur is set high, and a transition on the filter ensures that as the page loads, the blur gradually reduces, creating a smooth, visually appealing reveal of the background.
The .loading-text class shows the loading percentage over the background. Its opacity starts at full visibility, and the transition on opacity makes the text fade out smoothly as the page progresses. Together, the blur on the background and the fading text combine to create a polished blurry loading effect that feels fluid and dynamic.
/* Basic CSS Reset */
...
.bg {
    /* linear-gradient puts a 'filter' to reduce the brightness of the bg image */
	background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)),
		url('YourImgUrlHere') no-repeat center center/cover;
	position: absolute;
    /* blur gets rough around the edges, so making the bg offset and expanding it to make the edges crisper */
	top: -30px;
	left: -30px;
	width: calc(100vw + 60px);
	height: calc(100vh + 60px);
	z-index: -1;
	filter: blur(30px);
	transition: filter 0.2s ease;
}
.loading-text {
	font-size: 50px;
	color: #fff;
	opacity: 1;
	transition: opacity 0.2s ease;
}The JavaScript drives the blurry loading effect by incrementally updating the visual elements as the page "loads". A numeric load value increases gradually from 0 to 100, and this value is used to control both the blur of the background and the opacity of the loading text.
The scale function takes a number and maps it from one range to another, allowing smooth transitions between the starting and ending values for both blur and opacity. This ensures that as load increases, the background gradually becomes sharper while the text fades out in a fluid, visually pleasing way.
To prevent any unexpected visual glitches, load is clamped using Math.min so it never exceeds MAX_LOAD. When the loading reaches 100%, the text is cleared and hidden after a brief delay. This is also a natural place to add a callback for triggering other page content once loading is complete (see my comment about this).
Finally, the code uses requestAnimationFrame recursively rather than setInterval. This allows the browser to sync updates with its repaint cycle, resulting in smoother and more efficient animations. Overall, the JavaScript orchestrates the visual flow of the loading screen while keeping the logic minimal and maintainable.
const loadText = document.querySelector('.loading-text');
const bgImg = document.querySelector('.bg');
let load = 0;
const MAX_LOAD = 100;
const MAX_BLUR = 30;
// takes in a number and its min and max values, then balances the transition for a range
const scale = (num, in_min = 0, in_max = MAX_LOAD, out_min = 0, out_max = 1) =>
	((num - in_min) * (out_max - out_min)) / (in_max - in_min) + out_min;
const animate = () => {
	// clamp for the load value to ensure it never exceeds MAX_LOAD to prevent unintended styles in css
	load = Math.min(load + 1, MAX_LOAD);
	// set load text to empty string then remove from page when load reaches MAX_LOAD, and stop recurssion
	if (load === MAX_LOAD) {
		// this could be expanded to use a callback for displaying other elements on the page when load completes
		setTimeout(() => {
			loadText.innerText = '';
			loadText.style.display = 'none';
		}, 500);
		return;
	}
	loadText.innerText = `${load}%`;
	loadText.style.opacity = scale(load, 0, MAX_LOAD, 1, 0);
	bgImg.style.filter = `blur(${scale(load, 0, MAX_LOAD, MAX_BLUR, 0)}px)`;
	// instead of setInterval, using requestAnimationFrame recursively for smoother animations
	requestAnimationFrame(animate);
};
requestAnimationFrame(animate);This project demonstrates how a small amount of carefully written JavaScript can create a visually engaging loading experience. By progressively updating the blur and opacity, the page provides clear feedback on load progress while maintaining smooth, polished animations. The scale function, combined with requestAnimationFrame, ensures the transitions feel natural, and the code remains minimal and easy to maintain. With optional callbacks, this setup can easily be extended to trigger other elements on the page once loading is complete, making it both flexible and accessible.
Jeremy is a Fullstack Developer. Originally from the United States, he currently resides in Taiwan, and works freelance remotely.
Portfolio