#image-mirror-tool {
max-width: 950px;
margin: 20px auto;
padding: 25px;
background: #f3f6fb;
border-radius: 15px;
font-family: Arial, sans-serif;
color: #1e293b;
box-sizing: border-box;
}
#image-mirror-tool * { box-sizing: border-box; }
#image-mirror-tool h2 {
text-align: center;
font-size: 27px;
color: #173b70;
margin-bottom: 10px;
}
#image-mirror-tool .subtitle {
text-align: center;
color: #64748b;
line-height: 1.6;
margin-bottom: 25px;
}
#image-mirror-tool .upload-area {
padding: 30px 15px;
border: 2px dashed #2563eb;
background: white;
border-radius: 12px;
text-align: center;
}
#image-mirror-tool input[type=file] {
max-width: 100%;
margin-top: 12px;
}
#image-mirror-tool .section-title {
font-size: 19px;
font-weight: bold;
margin: 23px 0 13px;
color: #173b70;
}
#image-mirror-tool .button-grid {
display: grid;
grid-template-columns: repeat(2,1fr);
gap: 12px;
}
#image-mirror-tool button {
border: none;
border-radius: 8px;
padding: 14px 10px;
background: #2563eb;
color: white;
font-size: 15px;
font-weight: bold;
cursor: pointer;
transition: .2s;
}
#image-mirror-tool button:hover {
filter: brightness(.9);
transform: translateY(-2px);
}
#image-mirror-tool button:disabled {
opacity: .5;
cursor: not-allowed;
transform: none;
}
#image-mirror-tool .horizontal-btn { background: #0f766e; }
#image-mirror-tool .vertical-btn { background: #9333ea; }
#image-mirror-tool .format-buttons {
display: grid;
grid-template-columns: repeat(5,1fr);
gap: 9px;
}
#image-mirror-tool .format-buttons button {
background: #334155;
padding: 12px 5px;
}
#image-mirror-tool button.active {
background: #16a34a;
}
#image-mirror-tool .preview-container {
display: none;
background: white;
border-radius: 12px;
padding: 18px;
margin-top: 22px;
}
#image-mirror-tool .image-comparison {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
text-align: center;
}
#image-mirror-tool .image-card {
padding: 12px;
background: #f1f5f9;
border-radius: 10px;
min-width: 0;
}
#image-mirror-tool .image-card img {
max-width: 100%;
max-height: 280px;
object-fit: contain;
margin-top: 10px;
}
#image-mirror-tool .download-btn {
display: inline-block;
background: #16a34a;
color: white;
text-decoration: none;
padding: 14px 25px;
border-radius: 8px;
font-weight: bold;
margin-top: 18px;
}
#image-mirror-tool .download-btn:hover {
background: #15803d;
}
#image-mirror-tool .status {
text-align: center;
margin-top: 16px;
color: #2563eb;
font-weight: bold;
line-height: 1.6;
}
#image-mirror-tool .note {
font-size: 13px;
color: #64748b;
line-height: 1.7;
margin-top: 20px;
}
@media(max-width:550px) {
#image-mirror-tool { padding: 14px; }
#image-mirror-tool h2 { font-size: 22px; }
#image-mirror-tool .format-buttons {
grid-template-columns: repeat(3,1fr);
}
#image-mirror-tool .image-comparison {
grid-template-columns: 1fr;
}
}
Online Image Mirror Tool
Mirror or flip your images horizontally and vertically. Supports JPG, PNG, GIF, WEBP and BMP.
Upload Your Image
Select an image from your device
Select Mirror Direction
Select Output Image Format
Note: Images are processed locally in your browser. Image dimensions remain unchanged. GIF animation requires a dedicated animated GIF processing library. For JPEG and WEBP, re-encoding can slightly affect quality. PNG export preserves pixel quality.
(function () { const root = document.getElementById("image-mirror-tool"); const upload = root.querySelector("#mirror-upload"); const status = root.querySelector("#mirror-status"); const preview = root.querySelector("#mirror-preview"); const originalPreview = root.querySelector("#mirror-original"); const resultPreview = root.querySelector("#mirror-result"); const download = root.querySelector("#mirror-download"); let image = null; let originalURL = null; let resultURL = null; let fileName = "image"; let selectedFormat = "image/png"; let flipX = false; let flipY = false; const extensions = { "image/jpeg": "jpg", "image/png": "png", "image/gif": "gif", "image/webp": "webp", "image/bmp": "bmp" }; function setBusy(busy) { root.querySelectorAll("button").forEach(button => { button.disabled = busy; }); } function formatSize(bytes) { return bytes { button.addEventListener("click", function () { selectedFormat = this.dataset.format; root.querySelectorAll("[data-format]").forEach(btn => { btn.classList.remove("active"); }); this.classList.add("active"); if (selectedFormat === "image/gif") { status.textContent = "GIF output selected. Animated GIF support requires a GIF encoder."; } else { status.textContent = "Output format selected: " + extensions[selectedFormat].toUpperCase(); } }); }); function mirror(horizontal) { if (!image) { status.textContent = "Please upload an image first."; return; } if (horizontal) { flipX = !flipX; } else { flipY = !flipY; } createMirror(); } async function createMirror() { if (!image) return; setBusy(true); status.textContent = "Mirroring image..."; try { if (selectedFormat === "image/gif") { throw new Error( "Animated GIF mirroring is not available in this basic version. " + "Use a GIF encoder to mirror every animation frame." ); } const canvas = document.createElement("canvas"); canvas.width = image.naturalWidth; canvas.height = image.naturalHeight; const ctx = canvas.getContext("2d"); ctx.save(); ctx.translate( flipX ? canvas.width : 0, flipY ? canvas.height : 0 ); ctx.scale(flipX ? -1 : 1, flipY ? -1 : 1); ctx.drawImage( image, 0, 0, canvas.width, canvas.height ); ctx.restore(); let blob; if (selectedFormat === "image/bmp") { blob = await exportBMP(canvas); } else { blob = await new Promise(resolve => { canvas.toBlob(resolve, selectedFormat, 0.95); }); } if (!blob) { throw new Error( "This browser does not support the selected output format." ); } if (resultURL) URL.revokeObjectURL(resultURL); resultURL = URL.createObjectURL(blob); resultPreview.src = resultURL; download.href = resultURL; download.download = fileName + "-mirrored." + extensions[selectedFormat]; preview.style.display = "block"; root.querySelector("#mirror-info").textContent = "Dimensions: " + canvas.width + " × " + canvas.height + " pixels | File Size: " + formatSize(blob.size) + " | Format: " + extensions[selectedFormat].toUpperCase(); status.textContent = "Mirrored image is ready to download."; } catch (error) { status.textContent = error.message; } finally { setBusy(false); } } // Export a 24-bit BMP file without lossy compression. function exportBMP(canvas) { return new Promise(resolve => { const width = canvas.width; const height = canvas.height; const ctx = canvas.getContext("2d"); const imageData = ctx.getImageData(0, 0, width, height); const pixels = imageData.data; const rowSize = Math.ceil(width * 3 / 4) * 4; const pixelSize = rowSize * height; const buffer = new ArrayBuffer(54 + pixelSize); const view = new DataView(buffer); const put16 = (offset, value) => view.setUint16(offset, value, true); const put32 = (offset, value) => view.setUint32(offset, value, true); view.setUint8(0, 66); view.setUint8(1, 77); put32(2, 54 + pixelSize); put32(10, 54); put32(14, 40); put32(18, width); put32(22, height); put16(26, 1); put16(28, 24); put32(34, pixelSize); const output = new Uint8Array(buffer); for (let y = 0; y < height; y++) { for (let x = 0; x mirror(true)); root.querySelector("#mirror-vertical") .addEventListener("click", () => mirror(false)); })();