Open In App

Creating a Simple Image Editor using JavaScript

Last Updated : 09 May, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

In this article, we will be creating a Simple Image Editor that can be used to adjust the image values like brightness, contrast, hue, saturation, grayscale, and sepia. Image editors allow one to quickly edit pictures after they have been captured for enhancing them or completely changing their look. Using a combination of various values, one can give an image a completely new look. We will be doing all this in-browser using only HTML, CSS, JavaScript, and the Canvas API making the application lightweight and fast.

Simple Image Editor

Here, we will learn how to select a file from our local system and use it on the canvas for making changes. We will also learn how to save the modified image to our system.

We have divided this task into 3 sections, ie., HTML, CSS & Javascript sections. We will understand the making of Image Editor in these sections in a sequential manner. 

HTML Layout: The HTML layout defines the element structure that would be shown on the page. This includes:

  • Header: This will contain the application title and buttons for saving the modified image or resetting the filters.
  • Image Preview: This section will hold the canvas that we will be working on. It will help to preview the image as we are changing the filter values.
  • Filter Sliders: This section will have all the sliders that will handle the image filter values.
  • Preset Filters: This section contains a set of buttons that can be used for setting multiple values at once for achieving a particular style.
  • File Controls Area: This section contains a button for selecting a new image from the file system.

We will be using the MaterializeCSS library for styling the application and positioning the content using its grid system to ensure mobile responsiveness.

Example: In this example, we will see the use of MatrializeCSS library.

HTML




<!DOCTYPE html>
<html lang="en">
 
<head>
    <title>Simple Image Editor</title>
    <link rel="stylesheet" href=
 
    <!-- Compiled and minified CSS -->
    <link rel="stylesheet" href=
   
    <!-- Import custom stylesheet -->
    <link rel="stylesheet" href="style.css">
 
    <!-- Compiled and minified JavaScript -->
    <script defer src=
    </script>
</head>
 
<body>
    <nav class="green">
        <div class="nav-wrapper container">
            <span class="nav-header">
              Simple Image Filters
            </span>
            <ul class="right image-save">
                <button class="btn btn-flat blue white-text"
                        onclick="saveImage()">
                  Save
                </button>
                <button class="btn btn-flat red white-text"
                        onclick="resetImage()">
                  Reset
                </button>
            </ul>
        </div>
    </nav>
 
    <!-- Hidden image that will be used for
    holding the source image -->
    <img id="sourceImage" crossorigin="anonymous">
 
    <div class="image-preview">
 
        <!-- Canvas that will hold the
        image to be edited -->
        <canvas id="canvas" height="0"></canvas>
    </div>
    <div class="container app">
 
        <!-- Text to be shown at the beginning
        of the application -->
        <div class="help-text center-align">
            <h5>Please Upload an Image to Start Editing</h5>
        </div>
 
        <!-- All the image controls that will be
        used for modifying the image -->
        <div class="image-controls">
            <h6>Image Controls</h6>
            <div class="row">
                <div class="col s6">
                    <span class="range-field">
                        <label for="brightnessSlider">
                          Brightness
                        </label>
                        <input id="brightnessSlider"
                               type="range" value="100"
                               min="0" max="300"
                               onchange="applyFilter()">
                    </span>
                </div>
                <div class="col s6">
                    <span class="range-field">
                        <label for="contrastSlider">
                          Contrast
                        </label>
                        <input id="contrastSlider"
                               type="range" value="100"
                               min="0" max="200"
                               onchange="applyFilter()">
                    </span>
                </div>
            </div>
 
            <div class="row">
                <div class="col s6">
                    <span class="range-field">
                        <label for="grayscaleSlider">
                          Grayscale
                        </label>
                        <input id="grayscaleSlider"
                               type="range" value="0"
                               min="0" max="100"
                               onchange="applyFilter()">
                    </span>
                </div>
                <div class="col s6">
                    <span class="range-field">
                        <label for="saturationSlider">
                          Saturation
                        </label>
                        <input id="saturationSlider"
                               type="range" value="100"
                               min="0" max="300"
                               onchange="applyFilter()">
                    </span>
                </div>
            </div>
 
            <div class="row">
                <div class="col s6">
                    <span class="range-field">
                        <label for="sepiaSlider">
                          Sepia
                        </label>
                        <input id="sepiaSlider"
                               type="range" value="0"
                               min="0" max="200"
                               onchange="applyFilter()">
                    </span>
                </div>
                <div class="col s6">
                    <span class="range-field">
                        <label for="hueRotateSlider">
                          Hue
                        </label>
                        <input id="hueRotateSlider"
                               type="range" value="0"
                               min="0" max="360"
                               onchange="applyFilter()">
                    </span>
                </div>
            </div>
        </div>
 
        <!-- Buttons that will be used to change
        the values to preset ones -->
        <div class="preset-filters">
            <h6>Preset Filters</h6>
            <button class="btn green"
                    onclick="brightenFilter()">
              Brighten
            </button>
            <button class="btn green"
                    onclick="bwFilter()">
              Black and White
            </button>
            <button class="btn green"
                    onclick="funkyFilter()">
              Funky
            </button>
            <button class="btn green"
                    onclick="vintageFilter()">
              Vintage
            </button>
        </div>
 
        <!-- File control to upload a new file -->
        <div class="file-controls">
            <h6>File Controls</h6>
           
            <!-- Element that will be later used to download
            the canvas image from code -->
            <a id="link"></a>
           
            <!-- File Selector for uploading the image -->
            <div class="file-field input-field">
                <div class="btn green">
                    <span>Upload Image</span>
                    <input type="file" accept="image/*"
                           onchange="uploadImage(event)">
                </div>
                <div class="file-path-wrapper">
                    <input class="file-path" type="text">
                </div>
            </div>
        </div>
    </div>
   
      <!-- Load the script for the editor -->
    <script src="app.js"></script>
</body>
 
</html>


CSS Styling: 

We will make some minor changes along with the default Materialize styles according to our applications. The main changes include:

  • Hiding the image filter sliders and buttons when an image has not been selected and showing a help message instead using the display property.
  • Centering the Image Preview by using the flex and justify-content properties.
  • Setting the maximum height of the canvas to a fixed value and using the object-fit property to make sure that it fits in the given area. This will help to use the application on all screen sizes without the image occupying too much space.

CSS




.nav-header {
    font-size: 1.5rem;
}
 
.row {
    margin-bottom: 0;
}
 
#sourceImage,
.image-controls,
.image-save,
.preset-filters {
    display: none;
}
 
.image-preview {
    display: flex;
    justify-content: center;
    margin-top: 20px;
}
 
#canvas {
    max-height: 420px;
    object-fit: contain;
}


JavaScript Section: 

The section contains the main logic of the application. Here, we will divide the logic into 5 steps, which are described below:

Step 1: Defining the variables and creating a reference to the HTML elements:

We will first select all the elements that need to be modified by JavaScript using the querySelector() method. They are then assigned variable names so that they could be accessed easily. A reference to the canvas and its 2D context is also created for drawing the image.

Javascript




// Get the source image to be edited
let image = document.getElementById('sourceImage');
 
// Get the canvas for the edited image
let canvas = document.getElementById('canvas');
 
// Get the 2D context of the image
let context = canvas.getContext('2d');
 
// Get all the sliders of the image
let brightnessSlider = document.getElementById("brightnessSlider");
let contrastSlider = document.getElementById("contrastSlider");
let grayscaleSlider = document.getElementById("grayscaleSlider");
let hueRotateSlider = document.getElementById("hueRotateSlider");
let saturateSlider = document.getElementById("saturationSlider");
let sepiaSlider = document.getElementById("sepiaSlider");


Step 2: Loading an image from the file system:

  • We will use the invisible <img> element, which we have defined in the HTML as the source image before drawing it to the canvas.
  • In the uploadImage() function, we will first load the image using the URL.createObjectURL() method and assign it to the src property of the above-mentioned source image element. The image file can be accessed using event.target.files[0] from the change event of the file selector.
  • We will then set the height and width of the canvas to be equal to the image’s dimensions. We will also set the crossOrigin property to anonymous to prevent the tainted canvas problem. We will then call the applyFilter() function for drawing the image to canvas. This function is explained in the next step.
  • We will also make the image controls visible and hide the help text that was displayed earlier.

Javascript




function uploadImage(event) {
 
    // Set the source of the image from the uploaded file
    image.src = URL.createObjectURL(event.target.files[0]);
 
    image.onload = function () {
        // Set the canvas the same width and height of the image
        canvas.width = this.width;
        canvas.height = this.height;
        canvas.crossOrigin = "anonymous";
        applyFilter();
    };
 
    // Show the image editor controls and hide the help text
    document.querySelector('.help-text').style.display = "none";
    document.querySelector('.image-save').style.display = "block";
    document.querySelector('.image-controls').style.display = "block";
    document.querySelector('.preset-filters').style.display = "block";
};


Step 3: Drawing the image and applying the current filter values.

  • The applyFilter() function: This is the main function that handles the drawing of the image and applying the filter values. The Canvas API has a filter property that applies filters to the canvas. As the property can only be assigned once, we will have to specify all the filters at the same time so that they get applied together.
  • We will start by creating the string that will contain the filters we have chosen for this application. These are the common filters that are available in CSS. We will get the current values of the sliders using the value property and use it in the string at the correct places.
  • We will then assign this string to the filter property of the canvas. This will assign apply the filters to the canvas.
  • Finally, we will draw the image using the drawImage() method of the Context API.

Javascript




// This function is used to update the image
// along with all the filter values
function applyFilter() {
 
    // Create a string that will contain all the filters
    // to be used for the image
    let filterString =
        "brightness(" + brightnessSlider.value + "%" +
        ") contrast(" + contrastSlider.value + "%" +
        ") grayscale(" + grayscaleSlider.value + "%" +
        ") saturate(" + saturateSlider.value + "%" +
        ") sepia(" + sepiaSlider.value + "%" +
        ") hue-rotate(" + hueRotateSlider.value + "deg" + ")";
 
    // Apply the filter to the image
    context.filter = filterString;
 
    // Draw the edited image to canvas
    context.drawImage(image, 0, 0);
}


Step 4: Using preset filters for the image:

  • Each of the preset filter buttons we have defined above can be used to set a certain value for each of the sliders. This will apply a unique combination of values that can be used for quickly achieving a look and then further modifying the values as needed.
  • Each of the functions will first reset the image by using the resetImage() function we will define in the next step, then set the required filter values for that preset and then call applyFilter() for redrawing the image with the filters.
  • The user can then change the sliders after the preset has been applied for further changing the look of the filter.

Javascript




// A series of functions that handle the preset filters
// Each of these will first reset the image
// and then apply a certain parameter before
// redrawing the image
function brightenFilter() {
    resetImage();
    brightnessSlider.value = 130;
    contrastSlider.value = 120;
    saturateSlider.value = 120;
    applyFilter();
}
 
function bwFilter() {
    resetImage();
    grayscaleSlider.value = 100;
    brightnessSlider.value = 120;
    contrastSlider.value = 120;
    applyFilter();
}
 
function funkyFilter() {
    resetImage();
 
    // Set a random hue rotation everytime
    hueRotateSlider.value =
        Math.floor(Math.random() * 360) + 1;
    contrastSlider.value = 120;
    applyFilter();
}
 
function vintageFilter() {
    resetImage();
    brightnessSlider.value = 120;
    saturateSlider.value = 120;
    sepiaSlider.value = 150;
    applyFilter();
}


Step 5: Resetting and Saving the image:

  • Reset Image: The image can be reset by setting the filter values to their default values and then calling the applyFilter() method to redraw the original image. This will also adjust the sliders to their default positions.
  • Saving Image: The image can be saved by referring to the temporary <a> element we have created in the HTML section. We will use the download property for setting the name of the file and the href property for setting the canvas data to be saved. We will convert the canvas to a data URL using the toDataURL() method and then to an image stream so that it starts downloading automatically.

Javascript




// Reset all the slider values to there default values
function resetImage() {
    brightnessSlider.value = 100;
    contrastSlider.value = 100;
    grayscaleSlider.value = 0;
    hueRotateSlider.value = 0;
    saturateSlider.value = 100;
    sepiaSlider.value = 0;
    applyFilter();
}
 
function saveImage() {
    // Select the temporary element we have created for
    // helping to save the image
    let linkElement = document.getElementById('link');
    linkElement.setAttribute(
      'download', 'edited_image.png'
    );
 
    // Convert the canvas data to a image data URL
    let canvasData = canvas.toDataURL("image/png")
 
    // Replace it with a stream so that
    // it starts downloading
    canvasData.replace(
      "image/png", "image/octet-stream"
    )
 
    // Set the location href to the canvas data
    linkElement.setAttribute('href', canvasData);
 
    // Click on the link to start the download
    linkElement.click();
}


After completing the above steps, the image editor is ready to use in any browser. You can add more preset filters as per your liking or even use the other available CSS filters.

Output:

Simple Image Editor



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads