<p>This demo will generate whole image as box-shadow in single div. Upload files at your own risk since it may freeze the browser. Try with small image first.</p>
<p>I've tried one from <a href="https://placekitten.com/">placekitten.com</a> I was able to copy the style that had few MB of data and upload it to <a href="https://jcu.bi/kitten-css">jcu.bi/kitten-css</a></p>
<p>If you try to select div in dev tools it may freeze dev tools.</p>
<p>This is not a joke, see the code for details.</p>
<p>Here is a little bit story behind this hack <a href="https://dev.to/jcubic/how-i-ve-created-photo-in-single-div-css-45bm">How I've created Photo in single div CSS</a></p>
<br/>
<input id="input" type="file"/>
<br/>
<button id="download_btn" disabled>download</button>
<!-- putting the CSS into textarea was a mistake -->
<textarea id="output" hidden></textarea>
<br/>
<section id="wrapper">
    <div></div>
</section>
<script type="text/x-template" id="html"><!DOCTYPE html>
<html>
    <head>
        <title>CSS Drawing</title>
        <style>{{CSS}}</style>
        <meta name="generator" content="CSS Codepen Demo by Jakub T. Jankiewicz"/>
    </head>
    <body id="wrapper">
        <div></div>
    </body>
</html>
</script>
textarea {
    width: 100vw;
    height: 400px;
}
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
canvas.hidden = true;
const ctx = canvas.getContext('2d');

function to_hex(...args) {
    return '#' + args.map(num => {
        return num.toString(16).toUpperCase().padStart(2, '0');
    }).join('');
}
var output_html;

input.addEventListener('change', event => {
    const [file] = event.target.files;
    const max_loop = 10000;
    getPixelsFromFile(file).then(imageData => {
        const data = [];
        const { width, height } = imageData;
        let i = 0;
        for (const {r, g, b, x, y} of processColors(imageData)) {
            // hex colors may produce less data
            data.push(`${x+1}px ${y+1}px ${to_hex(r, g, b)}`);
            // loop guard, CodePen will not complain but
            // there may be too much data to process
            // if (i++ >= max_loop) {
            //    break;
            // }
        }
        const style = document.createElement('style');
        style.type = 'text/css';
        var css = `div {
            display: inline-block;
            width: 1px;
            height: 1px;
            box-shadow: ${data.join(',')};
        }
        #wrapper {
            display: inline-block;
            min-height: ${height + 10}px;
            min-width:${width + 10}px;
        }`;
          
        if (style.styleSheet) {
            style.styleSheet.cssText = css;
        } else {
            style.appendChild(document.createTextNode(css));
        }
        output_html = html.innerText.replace(/\{\{CSS\}\}/, css);
        download_btn.disabled = false;
        
        document.body.appendChild(style);
    });
});

download_btn.addEventListener('click', () => {
    download(output_html, "css_image.html");
})

function download(string, filename) {
    const blob = new Blob([string], {
        type: 'text/plain'
    });
    const url = URL.createObjectURL(blob);
    const li = document.createElement('a');
    li.download = filename;
    li.href = url;
    li.innerText = 'download';
    li.style.display = 'none';
    document.body.appendChild(li);
    li.click();
    document.body.removeChild(li);
    URL.revokeObjectURL(url);
}

function* processColors(imagedata) {
    const { data, width, height } = imagedata;
    let index = 0;
    const len = data.length;
    const PIXELS = 4;
    while (index < len) {
        const r = data[index];
        const g = data[index + 1];
        const b = data[index + 2];
        const i = index / PIXELS;
        const x = i % width;
        const y = Math.floor(i / width);
        yield { r, g, b, x, y};
        index += PIXELS;
    }
}

function getPixelsFromFile(file) {
    return getImage(file).then(getImageData);
}

function getImage(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = function() {
            resolve(reader.result);
        };
        reader.onerror = function() {
            reject();
        };
        reader.readAsDataURL(file);
    }).then(toImage);
}

function getImageData(img) {
    const { width, height } = img;
    canvas.width = width;
    canvas.height = height;
    ctx.drawImage(img, 0, 0);
    return ctx.getImageData(0, 0, width, height);
}

function toImage(dataURL) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.addEventListener('load', () => {
            resolve(img);
        });
        img.addEventListener('error', reject);
        img.src = dataURL;
    });
}

// this will not work because Single Origin Policy
// you can grab pixels from cross origin images
// img.src = 'https://placekitten.com/408/287';

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.