Please bookmark this page to avoid losing your image tool!

Image Metadata Extractor Tool

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
// Helper function to ensure exif-js is loaded.
// This creates a global promise to avoid multiple loads if processImage is called multiple times.
let exifJsLoadPromise = null;
function ensureExifJsIsLoaded() {
    // Check if EXIF object already exists (library loaded)
    if (typeof EXIF !== 'undefined') {
        return Promise.resolve();
    }

    // If not loaded and no load attempt is in progress, start loading
    if (!exifJsLoadPromise) {
        exifJsLoadPromise = new Promise((resolve, reject) => {
            const script = document.createElement('script');
            script.src = 'https://cdnjs.cloudflare.com/ajax/libs/exif-js/2.3.0/exif.js';
            script.async = true; // Load asynchronously

            script.onload = () => {
                if (typeof EXIF !== 'undefined') {
                    // console.log('exif-js loaded successfully.');
                    resolve();
                } else {
                    // console.error('exif-js loaded but EXIF object not found.');
                    exifJsLoadPromise = null; // Reset promise to allow retry on subsequent calls
                    reject(new Error('exif-js loaded but EXIF object not found.'));
                }
            };

            script.onerror = (event) => {
                // console.error('Failed to load exif-js script:', event);
                exifJsLoadPromise = null; // Reset promise to allow retry
                reject(new Error('Failed to load exif-js script. Check network connection and CDN availability.'));
            };

            document.head.appendChild(script);
        });
    }
    return exifJsLoadPromise;
}

/**
 * Processes an image to extract its EXIF metadata using exif-js.
 * @param {Image} originalImg - The JavaScript Image object. It's crucial that originalImg.src is set
 *                              and points to accessible image data (e.g., a data URL, blob URL, or
 *                              a same-origin URL). Cross-origin URLs will only work if CORS is enabled
 *                              on the server.
 * @returns {Promise<HTMLDivElement>} A Promise that resolves with an HTMLDivElement.
 *                                    This element contains a table of EXIF metadata if found,
 *                                    or a message indicating why data could not be extracted.
 */
async function processImage(originalImg) {
    try {
        await ensureExifJsIsLoaded();
    } catch (error) {
        // console.error("Error loading EXIF library:", error);
        const errorContainer = document.createElement('div');
        errorContainer.style.padding = '15px';
        errorContainer.style.color = '#D8000C'; // Error red
        errorContainer.style.backgroundColor = '#FFD2D2'; // Light red background
        errorContainer.style.border = '1px solid #D8000C';
        errorContainer.style.borderRadius = '5px';
        errorContainer.style.fontFamily = 'Arial, sans-serif';
        errorContainer.textContent = 'Critical Error: Could not load the EXIF parsing library. ' + error.message;
        return errorContainer;
    }

    return new Promise((resolve) => {
        // EXIF.getData is asynchronous. It takes the image element/object and a callback.
        // The callback function is executed once EXIF data is parsed (or fails to parse).
        // Inside the callback, 'this' refers to the image object, now augmented with an 'exifdata' property.
        EXIF.getData(originalImg, function() {
            // 'this' here refers to the 'originalImg' object, which exif-js mutates to add 'exifdata'.
            const allTags = EXIF.getAllTags(this);
            
            const metadataContainer = document.createElement('div');
            metadataContainer.style.fontFamily = 'Arial, Helvetica, sans-serif';
            metadataContainer.style.padding = '15px';
            metadataContainer.style.margin = '10px 0'; // Vertical margin
            metadataContainer.style.border = '1px solid #ccc';
            metadataContainer.style.borderRadius = '5px';
            metadataContainer.style.backgroundColor = '#f9f9f9';
            metadataContainer.style.maxWidth = '100%'; // Ensure it fits its parent
            metadataContainer.style.overflowX = 'auto'; // Add horizontal scroll for wide content

            // Check if exifdata was populated and if any tags were found
            if (!this.exifdata || Object.keys(allTags).length === 0) {
                const p = document.createElement('p');
                p.style.color = '#555';
                p.textContent = 'No EXIF data found in this image. This might be because: \n' +
                                '1. The image format does not typically support EXIF data (e.g., PNG, GIF, WebP without EXIF chunk).\n' +
                                '2. The EXIF data was stripped during editing or optimization.\n' +
                                '3. The image source is not accessible (e.g., due to Cross-Origin Resource Sharing (CORS) restrictions if a URL was used).';
                p.style.whiteSpace = 'pre-line'; // To respect newlines in textContent
                metadataContainer.appendChild(p);
                resolve(metadataContainer);
                return;
            }

            const h3 = document.createElement('h3');
            h3.textContent = 'Image Metadata (EXIF)';
            h3.style.marginTop = '0';
            h3.style.marginBottom = '15px';
            h3.style.color = '#333';
            h3.style.borderBottom = '1px solid #eee';
            h3.style.paddingBottom = '10px';
            metadataContainer.appendChild(h3);

            const table = document.createElement('table');
            table.style.width = '100%';
            table.style.borderCollapse = 'collapse';
            
            const thead = table.createTHead();
            const headerRow = thead.insertRow();
            
            const thProperty = document.createElement('th');
            thProperty.textContent = 'Property';
            thProperty.style.borderBottom = '2px solid #ddd';
            thProperty.style.padding = '12px 10px';
            thProperty.style.textAlign = 'left';
            thProperty.style.backgroundColor = '#efefef';
            thProperty.style.color = '#333';
            headerRow.appendChild(thProperty);

            const thValue = document.createElement('th');
            thValue.textContent = 'Value';
            thValue.style.borderBottom = '2px solid #ddd';
            thValue.style.padding = '12px 10px';
            thValue.style.textAlign = 'left';
            thValue.style.backgroundColor = '#efefef';
            thValue.style.color = '#333';
            headerRow.appendChild(thValue);

            const tbody = table.createTBody();
            const sortedKeys = Object.keys(allTags).sort(); // Sort keys for consistent order

            for (const key of sortedKeys) {
                if (allTags.hasOwnProperty(key)) {
                    let value = allTags[key];
                    let displayValue;
                    
                    if (typeof value === 'object') {
                        try {
                            displayValue = JSON.stringify(value);
                        } catch (e) {
                            displayValue = '[Unserializable Object]';
                        }
                    } else {
                        displayValue = String(value); // Ensure value is stringified
                    }

                    // Truncate very long strings for better readability
                    if (displayValue.length > 256) { 
                        displayValue = displayValue.substring(0, 256) + '... [truncated]';
                    }
                    
                    const tr = tbody.insertRow();
                    tr.style.borderBottom = '1px solid #eee'; // Lighter lines for rows

                    // Alternate row background color for readability
                    if (tbody.rows.length % 2 === 0) {
                        tr.style.backgroundColor = '#f9f9f9';
                    } else {
                         tr.style.backgroundColor = '#fff';
                    }


                    const tdProperty = tr.insertCell();
                    tdProperty.textContent = key;
                    tdProperty.style.padding = '10px';
                    tdProperty.style.fontWeight = '600'; // Slightly bolder
                    tdProperty.style.color = '#444';
                    tdProperty.style.verticalAlign = 'top'; //Align content to top

                    const tdValue = tr.insertCell();
                    tdValue.textContent = displayValue;
                    tdValue.style.padding = '10px';
                    tdValue.style.fontFamily = 'monospace, "Courier New", Courier';
                    tdValue.style.wordBreak = 'break-all'; // Prevent long unbroken strings from overflowing
                    tdValue.style.verticalAlign = 'top';
                    tdValue.style.color = '#666';
                }
            }
            metadataContainer.appendChild(table);
            resolve(metadataContainer);
        });
    });
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Metadata Extractor Tool allows users to extract and view detailed EXIF metadata from images. EXIF data is embedded within most photos taken with digital cameras or smartphones, and it includes information such as camera settings, GPS coordinates, date and time, and more. This tool is useful for photographers who want to analyze their images, for developers who need to access image metadata for applications, and for anyone interested in understanding the technical details behind their images.

Leave a Reply

Your email address will not be published. Required fields are marked *