Reducing the number of page components in your application is essential to optimizing its performance. Images, for instance, can be combined into a single file to keep the number of components low.
CSS sprites are a combination of several images into a single larger image, also known as a sprite sheet. CSS styles are then used to display relevant images on the sprite sheet. Using CSS sprites in your application reduces the number of image components that need to be loaded, thus building your pages faster.
This tutorial will review how to create a sprite generator tool that can be used in combining images for your application. See the full code for this post here on GitHub.
Jump ahead:
CSS sprite sheets combine several images into one file, also known as a sprite sheet. Images on a sprite sheet can be arranged horizontally, vertically, diagonally, or in rows and columns.
A single sprite sheet, instead of multiple images, is loaded into your application and the images on the file can be accessed using CSS positioning styles. The x
and y
coordinates of an image on the sprite sheet are determined and the image is displayed on the webpage using some styling.
Making an HTTP request is costly, and fetching several images from the server is costlier, which may affect the performance of your app, especially on heavy-traffic sites. Using CSS sprites is a common technique to optimize your site’s performance by reducing the number of requests made to fetch image components.
Some of the benefits of using CSS sprites include:
The image below shows an example of a sprite sheet used on Wikipedia’s homepage. The sprite elements are arranged vertically on the sheet:
The sprite elements are then displayed on the page using background-image
and background-position
styles, as shown below:
To generate sprites, you can make use of a CSS sprite generator tool. In the next section, I’ll walk you through the steps of creating such a tool.
We’ll create a simple CSS generator tool that allows you to upload several image files and generate a sprite sheet. The sprite sheet can then be downloaded and used to display images needed on your site.
The images below show the application preview. Images uploaded and the sprite sheet generated are previewed; we also generate the CSS styling needed to display a sprite element:
In your HTML file, add the code snippet below:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="author" content="Ivy Walobwa"> <meta name="description" content="CSS Sprite Generator"> <meta name="keywords" content="html, html5, css, javascript, sprite sheet, image map, icon map, generator, tool, online, converter"> <link rel="stylesheet" href="style.css"> <script src="index.js"></script> <title>CSS Sprite Generator</title> </head> <body> <h1>CSS Sprite Generator Tool</h1> <div> <p> Please upload your files</p> <h3>Uploaded files</h3> <input type="file" id="fileUploaderInput" name="file" multiple accept="image/png, image/jpeg, image/webp, image/gif, image/x-icon, image/bmp, image/tiff" > <div id="sprite-preview"> </div> </div> <div class="section"> <h3>Sprite Preview</h3> <button id="generateSpriteSheetButton">Generate Sprite Sheet</button> <div id="sprite-sheet"> </div> </div> <div class="section"> <h3>Sprite Download</h3> <button id="spriteSheetDownloadButton"> Download Sprite</button> </div> <div class="section"> <h3>CSS Styling</h3> <div id="cssCode"> </div> </body> </html> >
We’ve now added the elements needed to upload images, generate, and download the sprite sheet to the file. The image below shows the preview of our HTML file:
Next, we’ll add some functionality to our app. Create an index.js
file and add the code snippet below. Here, we access elements in the DOM and add event listeners to the interactive elements.
The createSpriteSheet
, downloadSprite
, and fileUploaderOnchange
methods will be described in the sections below:
let fileUploaderInput; let generateSpriteSheetButton; let spritePreview; let spriteSheetContainer; let spriteSheetDownloadButton; let loadedImages = []; let generatedSpriteSheet; let cssCode; window.onload = () => { fileUploaderInput = document.getElementById('fileUploaderInput'); generateSpriteSheetButton = document.getElementById('generateSpriteSheetButton'); spritePreview = document.getElementById('sprite-preview'); spriteSheetContainer = document.getElementById('sprite-sheet'); spriteSheetDownloadButton = document.getElementById('spriteSheetDownloadButton'); cssCode = document.getElementById('cssCode'); generateSpriteSheetButton.addEventListener('click', (event) => createSpriteSheet(loadedImages)) spriteSheetDownloadButton.addEventListener('click', (event) => downloadSprite()) if (fileUploaderInput) { fileUploaderInput.addEventListener('change', fileUploaderOnchange); } }
In the index.js
file, create a fileUploaderOnchange
function and add the code snippet below:
const fileUploaderOnchange = (event) => { const loadedFiles = Array.from(fileUploaderInput.files); loadedFiles.forEach((file) => { const reader = new FileReader(); reader.onload = (event) => { // Create new image element and give it the source of our file and alt const image = new Image(); image.src = event.target.result; // When the image is loaded, create a canvas element and draw the image on it image.onload = () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // Set the canvas width and height canvas.width = image.width * (100 / image.width); canvas.height = image.height * (100 / image.height); // Draw the image on the canvas ctx.drawImage(image, 0, 0, canvas.width, canvas.height); // Create a new image element and give it the source of our canvas const newImage = new Image(); newImage.src = canvas.toDataURL(); newImage.alt = file.name.replace(/\.(png|jfif|pjpeg|jpeg|pjp|jpg|webp|gif|ico|bmp|dib|tiff|tif)$/, ""); spritePreview.appendChild(newImage); loadedImages.push(image); } } reader.readAsDataURL(file); }) }
In this code block, we create an array of image files from the uploaded files. We then loop through each image and create a new FileReader
object.
Next, we create a new image from the uploaded file and create a 2D canvas and draw the image on it. Finally, we append a new image to our DOM and give it the canvas source.
Now that we have our image files uploaded, we can generate a sprite sheet. In the index.js
file, create the function createSpriteSheet
and add the code snippet below:
const createSpriteSheet = (images) => { // Determine Sprite Sheet Dimensions const totalImages = images.length; // Calculate the minimum required dimensions for the sprite sheet const cols = Math.ceil(Math.sqrt(totalImages)); const spriteHeight = Math.ceil(totalImages / cols) * images[0].height; const spriteWidth = cols * images[0].width; // Create Canvas const canvas = document.createElement('canvas'); canvas.width = spriteWidth; canvas.height = spriteHeight; const ctx = canvas.getContext('2d'); // Arrange Images on Canvas let x = 0; let y = 0; for (const image of images) { ctx.drawImage(image, x, y); x += image.width; if (x >= spriteWidth) { x = 0; y += image.height; } } // Generate CSS Styles let cssStyles = ''; x = 0; y = 0; for (let i = 0; i < totalImages; i++) { const image = images[i]; const className = `sprite-image-${i}`; cssStyles += ` .${className} { background-image: url('sprite-sheet.png') background-position: ${x * -1}px ${y * -1}px; width: ${image.width}px; height: ${image.height}px; } <br> `; x += image.width; if (x >= spriteWidth) { x = 0; y += image.height; } } const newImage = new Image(); newImage.src = canvas.toDataURL(); newImage.alt = 'sprite-sheet'; spriteSheetContainer.appendChild(newImage); generatedSpriteSheet = newImage cssCode.innerHTML = cssStyles; }
In the code block above, we determine the dimensions of our canvas by calculating the number of columns needed and the sprite width and height of our canvas. Our generator currently works best with square images of equivalent width and height.
Next, we create a 2D canvas with the dimensions we calculated and arrange the images on the canvas. We also generate the CSS styles to use for each image.
Finally, we create a new image and give it a URL of our sprite canvas. The sprite sheet image and CSS styles are added to the DOM.
To download the generated sprite, we’ll create a link
element and add the sprite image source as its href
, and invoke the link click:
const downloadSprite = () => { // Create a link element const link = document.createElement('a'); link.download = 'sprite-sheet.png'; // Add the image source to the link href link.href = generatedSpriteSheet.src; // Invoke the link click link.click(); }
The image below shows the downloaded sprite sheet:
Now that you have generated your sprite sheet, you can load it into your app and use CSS styles to display the images. The background-image
, background-position
, width
, and height
properties are set to select a specific image on the sprite sheet:
<div class="sprite-image-0"></div> <div class="sprite-image-1"></div> .sprite-image-0 { background-image: url('sprite-sheet.png'); background-position: 0px 0px; width: 500px; height: 500px; } .sprite-image-1 { background-image: url('sprite-sheet.png'); background-position: -500px 0px; width: 500px; height: 500px; }
In this tutorial, we discussed what a CSS sprite is and some of its benefits in improving app performance. We also created a simple CSS generator tool and learned how to use the generated sprite sheet in our app. The complete code used in this article is available on GitHub.
The technique used in this article is just one of many methods that can be used to reduce image components. By experimenting with different methods, you can decide which ones you prefer to optimize your app performance. Happy coding!
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowCompare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.