In this tutorial, I will teach you how to make an Image Editor Using JavaScript, HTML5, and CSS3. The complete source code of this JavaScript image editor is given below.
I have also provided a download link at the end of this article through which you can download the full source code (including image files).
JavaScript Image Editor Source Code
index.html
<!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <title>Image Editor JavaScript</title> <link rel="stylesheet" href="style.css"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="https://unpkg.com/[email protected]/css/boxicons.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css"/> </head> <body> <div class="container disable"> <h2>Easy Image Editor</h2> <div class="wrapper"> <div class="editor-panel"> <div class="filter"> <label class="title">Filters</label> <div class="options"> <button id="brightness" class="active">Brightness</button> <button id="saturation">Saturation</button> <button id="inversion">Inversion</button> <button id="grayscale">Grayscale</button> </div> <div class="slider"> <div class="filter-info"> <p class="name">Brighteness</p> <p class="value">100%</p> </div> <input type="range" value="100" min="0" max="200"> </div> </div> <div class="rotate"> <label class="title">Rotate & Flip</label> <div class="options"> <button id="left"><i class="fa-solid fa-rotate-left"></i></button> <button id="right"><i class="fa-solid fa-rotate-right"></i></button> <button id="horizontal"><i class='bx bx-reflect-vertical'></i></button> <button id="vertical"><i class='bx bx-reflect-horizontal' ></i></button> </div> </div> </div> <div class="preview-img"> <img src="image-placeholder.svg" alt="preview-img"> </div> </div> <div class="controls"> <button class="reset-filter">Reset Filters</button> <div class="row"> <input type="file" class="file-input" accept="image/*" hidden> <button class="choose-img">Choose Image</button> <button class="save-img">Save Image</button> </div> </div> </div> <script src="script.js"></script> </body> </html>
script.js
const fileInput = document.querySelector(".file-input"), filterOptions = document.querySelectorAll(".filter button"), filterName = document.querySelector(".filter-info .name"), filterValue = document.querySelector(".filter-info .value"), filterSlider = document.querySelector(".slider input"), rotateOptions = document.querySelectorAll(".rotate button"), previewImg = document.querySelector(".preview-img img"), resetFilterBtn = document.querySelector(".reset-filter"), chooseImgBtn = document.querySelector(".choose-img"), saveImgBtn = document.querySelector(".save-img"); let brightness = "100", saturation = "100", inversion = "0", grayscale = "0"; let rotate = 0, flipHorizontal = 1, flipVertical = 1; const loadImage = () => { let file = fileInput.files[0]; if(!file) return; previewImg.src = URL.createObjectURL(file); previewImg.addEventListener("load", () => { resetFilterBtn.click(); document.querySelector(".container").classList.remove("disable"); }); } const applyFilter = () => { previewImg.style.transform = `rotate(${rotate}deg) scale(${flipHorizontal}, ${flipVertical})`; previewImg.style.filter = `brightness(${brightness}%) saturate(${saturation}%) invert(${inversion}%) grayscale(${grayscale}%)`; } filterOptions.forEach(option => { option.addEventListener("click", () => { document.querySelector(".active").classList.remove("active"); option.classList.add("active"); filterName.innerText = option.innerText; if(option.id === "brightness") { filterSlider.max = "200"; filterSlider.value = brightness; filterValue.innerText = `${brightness}%`; } else if(option.id === "saturation") { filterSlider.max = "200"; filterSlider.value = saturation; filterValue.innerText = `${saturation}%` } else if(option.id === "inversion") { filterSlider.max = "100"; filterSlider.value = inversion; filterValue.innerText = `${inversion}%`; } else { filterSlider.max = "100"; filterSlider.value = grayscale; filterValue.innerText = `${grayscale}%`; } }); }); const updateFilter = () => { filterValue.innerText = `${filterSlider.value}%`; const selectedFilter = document.querySelector(".filter .active"); if(selectedFilter.id === "brightness") { brightness = filterSlider.value; } else if(selectedFilter.id === "saturation") { saturation = filterSlider.value; } else if(selectedFilter.id === "inversion") { inversion = filterSlider.value; } else { grayscale = filterSlider.value; } applyFilter(); } rotateOptions.forEach(option => { option.addEventListener("click", () => { if(option.id === "left") { rotate -= 90; } else if(option.id === "right") { rotate += 90; } else if(option.id === "horizontal") { flipHorizontal = flipHorizontal === 1 ? -1 : 1; } else { flipVertical = flipVertical === 1 ? -1 : 1; } applyFilter(); }); }); const resetFilter = () => { brightness = "100"; saturation = "100"; inversion = "0"; grayscale = "0"; rotate = 0; flipHorizontal = 1; flipVertical = 1; filterOptions[0].click(); applyFilter(); } const saveImage = () => { const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); canvas.width = previewImg.naturalWidth; canvas.height = previewImg.naturalHeight; ctx.filter = `brightness(${brightness}%) saturate(${saturation}%) invert(${inversion}%) grayscale(${grayscale}%)`; ctx.translate(canvas.width / 2, canvas.height / 2); if(rotate !== 0) { ctx.rotate(rotate * Math.PI / 180); } ctx.scale(flipHorizontal, flipVertical); ctx.drawImage(previewImg, -canvas.width / 2, -canvas.height / 2, canvas.width, canvas.height); const link = document.createElement("a"); link.download = "image.jpg"; link.href = canvas.toDataURL(); link.click(); } filterSlider.addEventListener("input", updateFilter); resetFilterBtn.addEventListener("click", resetFilter); saveImgBtn.addEventListener("click", saveImage); fileInput.addEventListener("change", loadImage); chooseImgBtn.addEventListener("click", () => fileInput.click());
style.css
/* Import Google font - Poppins */ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap'); *{ margin: 0; padding: 0; box-sizing: border-box; font-family: 'Poppins', sans-serif; } body{ display: flex; padding: 10px; min-height: 100vh; align-items: center; justify-content: center; background: #E3F2FD; } .container{ width: 850px; padding: 30px 35px 35px; background: #fff; border-radius: 10px; box-shadow: 0 10px 20px rgba(0,0,0,0.1); } .container.disable .editor-panel, .container.disable .controls .reset-filter, .container.disable .controls .save-img{ opacity: 0.5; pointer-events: none; } .container h2{ margin-top: -8px; font-size: 22px; font-weight: 500; } .container .wrapper{ display: flex; margin: 20px 0; min-height: 335px; } .wrapper .editor-panel{ padding: 15px 20px; width: 280px; border-radius: 5px; border: 1px solid #ccc; } .editor-panel .title{ display: block; font-size: 16px; margin-bottom: 12px; } .editor-panel .options, .controls{ display: flex; flex-wrap: wrap; justify-content: space-between; } .editor-panel button{ outline: none; height: 40px; font-size: 14px; color: #6C757D; background: #fff; border-radius: 3px; margin-bottom: 8px; border: 1px solid #aaa; } .editor-panel .filter button{ width: calc(100% / 2 - 4px); } .editor-panel button:hover{ background: #f5f5f5; } .filter button.active{ color: #fff; border-color: #5372F0; background: #5372F0; } .filter .slider{ margin-top: 12px; } .filter .slider .filter-info{ display: flex; color: #464646; font-size: 14px; justify-content: space-between; } .filter .slider input{ width: 100%; height: 5px; accent-color: #5372F0; } .editor-panel .rotate{ margin-top: 17px; } .editor-panel .rotate button{ display: flex; align-items: center; justify-content: center; width: calc(100% / 4 - 3px); } .rotate .options button:nth-child(3), .rotate .options button:nth-child(4){ font-size: 18px; } .rotate .options button:active{ color: #fff; background: #5372F0; border-color: #5372F0; } .wrapper .preview-img{ flex-grow: 1; display: flex; overflow: hidden; margin-left: 20px; border-radius: 5px; align-items: center; justify-content: center; } .preview-img img{ max-width: 490px; max-height: 335px; width: 100%; height: 100%; object-fit: contain; } .controls button{ padding: 11px 20px; font-size: 14px; border-radius: 3px; outline: none; color: #fff; cursor: pointer; background: none; transition: all 0.3s ease; text-transform: uppercase; } .controls .reset-filter{ color: #6C757D; border: 1px solid #6C757D; } .controls .reset-filter:hover{ color: #fff; background: #6C757D; } .controls .choose-img{ background: #6C757D; border: 1px solid #6C757D; } .controls .save-img{ margin-left: 5px; background: #5372F0; border: 1px solid #5372F0; } @media screen and (max-width: 760px) { .container{ padding: 25px; } .container .wrapper{ flex-wrap: wrap-reverse; } .wrapper .editor-panel{ width: 100%; } .wrapper .preview-img{ width: 100%; margin: 0 0 15px; } } @media screen and (max-width: 500px) { .controls button{ width: 100%; margin-bottom: 10px; } .controls .row{ width: 100%; } .controls .row .save-img{ margin-left: 0px; } }