Safely Process Images in The Browser Without Memory Overflows

The blog raises questions about how to reduce the risk of memory overflow while processing images in a browser when users pick several images from device storage simultaneously. A common pitfall is browser memory overflow, which often renders the application unresponsive and, in the worst-case scenario, leads to a device reload. I hope to provide some useful examples that help prevent such issues.

A Short Disclaimer

Before we start, I want to point out that I strongly recommend against implementing image conversion on the front-end. Front-end devices are unpredictable: we can’t anticipate each user’s memory capacity, CPU, GPU, or device platform, meaning users will experience different processing times and outcomes. It’s better to leave this task to the backend, where resource parameters are more controllable and consistent.

Why Browser-Based Image Processing

Despite the drawbacks, there are cases where browser-side image processing is necessary. For example, when users need an immediate image preview before upload—particularly when certain image formats, like .heic/.heif from iOS devices, are not natively supported by browsers. In such cases, the application must convert the images before they can be previewed.

Issues with Compressing and Converting in Browser Memory

While a single, lightweight image doesn’t typically cause issues, what happens when users upload multiple large images—say 10 images, each up to 5 or even 10 MB? At the same time, we may want to compress or resize the images for efficiency. Even using well-known libraries like heic2any (for converting .heic files) or browser-image-compression (for resizing and compressing) won’t fully alleviate memory strain.

When dealing with multiple large images, the browser must store them all in memory at once. Additionally, associated operations, like canvas elements, require more memory allocation. Our task becomes reducing memory usage and clearing unnecessary objects on the fly.Initial Solution for Compression and Convertion Images

Lets ask ChatGPT as a co-pilot for code example to implement processing of several chosen images:

import imageCompression from 'browser-image-compression';
import heic2any from 'heic2any';

async function compressImages(filesArray) {

    // Create an array of promises to compress images in parallel
    const compressionPromises = filesArray.map(file => {
        let processedFile;

        if (file.type === 'image/heic' || file.type === 'image/heif') {
            // Convert HEIC to JPEG or PNG using heic2any
            processedFile = heic2any({
                blob: file,
                toType: 'image/jpeg',
                quality: 0.8 // Adjust quality to reduce memory usage
            });
        } else {
            // Compress the image using browser-image-compression
            const options = {
                maxSizeMB: 1, // Limit to 1 MB
                maxWidthOrHeight: 1024, // Resize to a maximum of 1024x1024
                useWebWorker: true // Use Web Workers for efficient processing
            };

            processedFile = imageCompression(file, options);
        }
        return processedFile;
    });

    try {
        // Wait for all compression tasks to complete
        const compressedFiles = await Promise.all(compressionPromises);
        return compressedFiles;
    } catch (error) {
        console.error('Error compressing images:', error);
    }
}

This solution is decent! It uses modern JavaScript concepts like declarative programming with array methods and parallel promise execution with Promise.all(). However, these solutions are the main causes of memory overflow. Let’s revise the function a bit.

Strategies for Improving Browser-Based Image Processing

1. Change the Programming Approach:

Instead of declarative iteration through the array, switch to an imperative style. Replace array iteration methods like .map() with a plain for loop. Array methods create closures and add tasks to the browser’s call stack, consuming extra memory.

From this approach:

const compressionPromises = filesArray.map(file => {
	// ...
});

To this approach:

for (const file of filesArray) {
	// ...
}

Or even this:

for (let i = 0; i < filesArray.length; i++) {
	// ...
}

2. Avoid Parallel Processing:

Replace Promise.all() with sequential processing. Promise.all() will attempt to process all files simultaneously, consuming memory equal to the sum of each file’s memory usage. By processing images one at a time, memory overflow risks are minimized. A sequential approach ensures that memory usage is kept under control.

While Promise.all() might finish faster, that speed comes at the cost of reliability. Offering user-friendly UI feedback—like a loader or file processing counter—can keep users informed while preventing memory issues.

Additionally, since Promise.all() rejects immediately if any promise fails, sequential processing allows for better error handling. Rather than restarting the entire process when one file fails, we can handle each failure individually.

3. Clear Memory After Each Step:

Remember to clear memory after every image is processed by revoking Blob URLs and other temporary objects:

URL.revokeObjectURL(URL.createObjectURL(file));

This explicitly tells the browser to free up memory associated with the file, allowing the garbage collector to remove unused objects from memory.

Final Version of Initial Solution

import imageCompression from 'browser-image-compression';
import heic2any from 'heic2any';

async function processImagesSequentially(filesArray) {
    const compressedImages = [];

    for (const file of filesArray) {
        let processedFile;

        if (file.type === 'image/heic' || file.type === 'image/heif') {
            // Convert HEIC to JPEG or PNG using heic2any
            const options = {
                blob: file,
                toType: 'image/jpeg',
                quality: 0.8  // Adjust quality to reduce memory usage
            };

            processedFile = await heic2any(options);
        } else {
            // Compress the image using browser-image-compression
            const options = {
                maxSizeMB: 1,  // Limit to 1 MB
                maxWidthOrHeight: 1024,  // Resize to a maximum of 1024x1024
                useWebWorker: true  // Use Web Workers for efficient processing
            };

            processedFile = await imageCompression(file, options);
        }

        compressedImages.push(processedFile);

        // Clear memory by revoking Blob URLs and other temporary objects
        URL.revokeObjectURL(URL.createObjectURL(file));
    }

    return compressedImages;
}

Conclusion

In summary, preventing browser memory overflow during image processing requires careful management of memory usage. The key strategies I’ve outlined above include processing images sequentially, reducing unnecessary memory consumption, and clearing memory after each step. While relying on the user’s browser for image processing isn’t ideal, these techniques can help mitigate risks in situations where it’s necessary.

If you’re looking for help building web applications that work with images, Trailhead can help. Contact us for a free consultation, and we’ll work with you to design solutions that optimize performance, ensure reliability, and prevent common pitfalls like memory overflow.

Picture of Alex Movchan

Alex Movchan

Alex started his professional path in finance and banking and devoted at least 15 years to the financial industry before switching to software engineering. Now he has over 7 years of experience as a full-stack developer of web applications. Alex is experienced in all the popular front-end Javascript frameworks, TypeScript, and plain old Javascript itself. He is also closely familiar with NodeJS as a backend technology. His academic background gives him an advantage in algorithms, statistics, and data analysis. Alex likes a result-oriented approach to software development, which he likes to execute in a reliable and solution-oriented manner. He likes to be up-to-date with the latest trends and best practices and even enjoys learning a little in connected spheres such as mobile development.

Free Consultation

Sign up for a FREE consultation with one of Trailhead's experts.

"*" indicates required fields

This field is for validation purposes and should be left unchanged.

Related Blog Posts

We hope you’ve found this to be helpful and are walking away with some new, useful insights. If you want to learn more, here are a couple of related articles that others also usually find to be interesting:

Our Gear Is Packed and We're Excited to Explore With You

Ready to come with us? 

Together, we can map your company’s software journey and start down the right trails. If you’re set to take the first step, simply fill out our contact form. We’ll be in touch quickly – and you’ll have a partner who is ready to help your company take the next step on its software journey. 

We can’t wait to hear from you! 

Main Contact

This field is for validation purposes and should be left unchanged.

Together, we can map your company’s tech journey and start down the trails. If you’re set to take the first step, simply fill out the form below. We’ll be in touch – and you’ll have a partner who cares about you and your company. 

We can’t wait to hear from you! 

Montage Portal

Montage Furniture Services provides furniture protection plans and claims processing services to a wide selection of furniture retailers and consumers.

Project Background

Montage was looking to build a new web portal for both Retailers and Consumers, which would integrate with Dynamics CRM and other legacy systems. The portal needed to be multi tenant and support branding and configuration for different Retailers. Trailhead architected the new Montage Platform, including the Portal and all of it’s back end integrations, did the UI/UX and then delivered the new system, along with enhancements to DevOps and processes.

Logistics

We’ve logged countless miles exploring the tech world. In doing so, we gained the experience that enables us to deliver your unique software and systems architecture needs. Our team of seasoned tech vets can provide you with:

Custom App and Software Development

We collaborate with you throughout the entire process because your customized tech should fit your needs, not just those of other clients.

Cloud and Mobile Applications

The modern world demands versatile technology, and this is exactly what your mobile and cloud-based apps will give you.

User Experience and Interface (UX/UI) Design

We want your end users to have optimal experiences with tech that is highly intuitive and responsive.

DevOps

This combination of Agile software development and IT operations provides you with high-quality software at reduced cost, time, and risk.

Trailhead stepped into a challenging project – building our new web architecture and redeveloping our portals at the same time the business was migrating from a legacy system to our new CRM solution. They were able to not only significantly improve our web development architecture but our development and deployment processes as well as the functionality and performance of our portals. The feedback from customers has been overwhelmingly positive. Trailhead has proven themselves to be a valuable partner.

– BOB DOERKSEN, Vice President of Technology Services
at Montage Furniture Services

Technologies Used

When you hit the trails, it is essential to bring appropriate gear. The same holds true for your digital technology needs. That’s why Trailhead builds custom solutions on trusted platforms like .NET, Angular, React, and Xamarin.

Expertise

We partner with businesses who need intuitive custom software, responsive mobile applications, and advanced cloud technologies. And our extensive experience in the tech field allows us to help you map out the right path for all your digital technology needs.

  • Project Management
  • Architecture
  • Web App Development
  • Cloud Development
  • DevOps
  • Process Improvements
  • Legacy System Integration
  • UI Design
  • Manual QA
  • Back end/API/Database development

We partner with businesses who need intuitive custom software, responsive mobile applications, and advanced cloud technologies. And our extensive experience in the tech field allows us to help you map out the right path for all your digital technology needs.

Our Gear Is Packed and We're Excited to Explore with You

Ready to come with us? 

Together, we can map your company’s tech journey and start down the trails. If you’re set to take the first step, simply fill out the contact form. We’ll be in touch – and you’ll have a partner who cares about you and your company. 

We can’t wait to hear from you! 

Thank you for reaching out.

You’ll be getting an email from our team shortly. If you need immediate assistance, please call (616) 371-1037.