Create a CSS sprite generator tool

Matt Angelosanto - Oct 3 '23 - - Dev Community

Written by Ivy Walobwa✏️

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:

Understanding CSS sprites

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:

  • Faster loading speed of your application because server requests are reduced: one sprite sheet request is made instead of several image requests
  • The performance of your application is improved and an increased loading speed means a better user experience
  • Reducing requests saves bandwidth, which is important for users with restricted data plans or slower internet connections

The image below shows an example of a sprite sheet used on Wikipedia’s homepage. The sprite elements are arranged vertically on the sheet: Sprite image example from Wikipedia The sprite elements are then displayed on the page using background-image and background-position styles, as shown below: Sprite image usage on a Wikipedia page 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.

Create a CSS sprite generator 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: CSS sprite sheet generator tool previewGenerated CSS style sprite elements

Getting started

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>
>
Enter fullscreen mode Exit fullscreen mode

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: A preview of the 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Upload images

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);
    })
}
Enter fullscreen mode Exit fullscreen mode

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.

Create the sprite sheet

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;
}
Enter fullscreen mode Exit fullscreen mode

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.

Download the generated sprite sheet

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();
}
Enter fullscreen mode Exit fullscreen mode

The image below shows the downloaded sprite sheet: Our downloaded sprite sheet

Use the 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;
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

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!


Is your frontend hogging your users' CPU?

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 Signup

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .