Meme Generator using JavaScript

Share your love

Hello there, fellow coders! Are you tired of browsing through endless memes and want to try your hand at creating your own? Look no further, because in this blog post, we are going to create a custom Meme Generator using the power of JavaScript! With the help of HTML, CSS, and some JavaScript magic, we’ll create a tool that will allow you to upload your own images, add text captions, and generate hilarious memes in no time. Not only will this project be a fun and creative endeavor, but you’ll also gain valuable experience in web development, including working with images, user input, and dynamic content generation. So let’s roll up our sleeves and get started on creating your very own Meme Generator!

Before we start here are some more projects you might like to create –

I would recommend you don’t just copy and paste the code, just look at the code and type by understanding it.

HTML Code 

Starter Template

<!doctype html>
<html lang="en">

<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">

    <!-- Font Awesome Icons  -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"
        integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA=="
        crossorigin="anonymous" />

    <!-- CSS -->
    <link rel="stylesheet" href="style.css">

    <title>Meme Generator using JavaScript - Coding Torque</title>
</head>

<body>
    <!-- Further code here -->

    <script src="script.js"></script>
</body>

</html>

Paste the below code in your <body> tag.

<div class="header">
    <h4>Meme Generator</h4>

    <div>
        <button id="export">CREATE</button>
    </div>

</div>
<div class="container">
    <div>
        <div id="canvasWrapper">
        </div>
    </div>

    <div class="memeForm">
        <h5>Source Image</h5>
        <div class="box">
            <div>
                <p>From URL</p>
                <div class="text_input">
                    <input id="imgURL" class="input" type="text" placeholder="Link to image" />
                </div>
            </div>
            <div>
                <p>Upload from your device</p>
                <input id="imgFile" type="file" accept="image/*" />
            </div>
        </div>

        <h4 style="margin-top: 50px;">Meme Text</h4>
        <div class="box">
            <div>
                <p>Top Text</p>
                <div class="text_input">
                    <input id="textTop" type="text" class="input" placeholder="Top text" />
                </div>
            </div>
            <div>
                <p>Bottom Text</p>
                <div class="text_input">
                    <input id="textBottom" type="text" class="input" placeholder="Bottom text" />
                </div>
            </div>
        </div>

        <h4>Text Size</h4>
        <div class="box">
            <div>
                <p>Top Text: <span id="textSizeTopOut">10</span></p>
                <div class="sliderContainer">
                    <input id="textSizeTop" type="range" min="2" max="50" step="2" />
                </div>
            </div>
            <div>
                <p>Bottom Text: <span id="textSizeBottomOut">10</span></p>
                <div class="sliderContainer">
                    <input id="textSizeBottom" type="range" min="2" max="50" step="2" />
                </div>
            </div>
        </div>

        <div class="box">
            <div>
                <h4>Preview Size</h4>
                <input id="trueSize" type="checkbox" />
                <label for="trueSize"><span>Show true size</span></label>
            </div>
        </div>
    </div>
</div>

Output Till Now

CSS Code 

Create a file style.css and paste the code below.

body {
  background: #1e293b;
  color: white;
}

.fullwidth {
  width: 100%;
  min-width: 400px;
  max-height: 800px;
}

.header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 5rem;
  background-color: #334155;
}

#export {
  background: #3b82f6;
  color: white;
  border: none;
  border-radius: 4px;
  padding: 5px 20px;
}

.container {
  display: flex;
  padding: 0.5rem 5rem;
}

.memeForm {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding-left: 2rem;
}

.box {
  display: flex;
}

.box div {
  margin-right: 30px;
}

.text_input {
  margin-bottom: auto;
  margin-top: auto;
  height: 50px;
  background-color: #353b48;
  border-radius: 30px;
  padding: 10px;
}

.input {
  color: white;
  border: 0;
  outline: 0;
  background: none;
  width: 250px;
  caret-color: transparent;
  line-height: 30px;
  transition: width 0.4s linear;
  padding: 0 10px;
}

.text_input:hover > .input {
  padding-right: 0 15px;
  caret-color: deepskyblue;
  transition: width 0.4s linear;
}

/* custom sliders for text sizes */
div.sliderContainer {
  width: 200px;
  text-align: center;
}

#textSizeTop,
#textSizeBottom {
  -webkit-appearance: none;
  appearance: none;
  height: 18px !important;
  width: 100%;
  border-radius: 10em;
  background-color: deepskyblue;
  outline: none;
  margin-bottom: 14px;
}

#textSizeBottom::-webkit-slider-thumb,
#textSizeTop::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 25px;
  height: 25px;
  border-radius: 50%;
  background: #3b82f6;
  cursor: pointer;
  border: 3px solid #f4f4f4;
}

#textSizeBottom::-moz-range-thumb,
#textSizeTop::-moz-range-thumb {
  width: 25px;
  height: 25px;
  border-radius: 50%;
  background-color: #dbc500;
  cursor: pointer;
  border: 3px solid #f4f4f4;
}

input[type="checkbox"] {
  display: none;
}

input[type="checkbox"] + label {
  display: block;
  position: relative;
  padding-left: 35px;
  margin-bottom: 20px;
  font: 14px/20px "Open Sans", Arial, sans-serif;
  color: #ddd;
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
}

input[type="checkbox"] + label:last-child {
  margin-bottom: 0;
}

input[type="checkbox"] + label:before {
  content: "";
  display: block;
  width: 20px;
  height: 20px;
  border: 2px solid #6cc0e5;
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0.6;
  -webkit-transition: all 0.12s, border-color 0.08s;
  transition: all 0.12s, border-color 0.08s;
}

input[type="checkbox"]:checked + label:before {
  width: 10px;
  top: -5px;
  left: 5px;
  border-radius: 0;
  opacity: 1;
  border-top-color: transparent;
  border-left-color: transparent;
  -webkit-transform: rotate(45deg);
  transform: rotate(45deg);
}

JavaScript Code 

Create a file script.js and paste the code below.

// CAN\NVAS.js plugin
// ninivert, december 2016
(function (window, document) {
    /**
    * CAN\VAS Plugin - Adding line breaks to canvas
    * @arg {string} [str=Hello World] - text to be drawn
    * @arg {number} [x=0]             - top left x coordinate of the text
    * @arg {number} [y=textSize]      - top left y coordinate of the text
    * @arg {number} [w=canvasWidth]   - maximum width of drawn text
    * @arg {number} [lh=1]            - line height
    * @arg {number} [method=fill]     - text drawing method, if 'none', text will not be rendered
    */

    CanvasRenderingContext2D.prototype.drawBreakingText = function (str, x, y, w, lh, method) {
        // local variables and defaults
        var textSize = parseInt(this.font.replace(/\D/gi, ''));
        var textParts = [];
        var textPartsNo = 0;
        var words = [];
        var currLine = '';
        var testLine = '';
        str = str || '';
        x = x || 0;
        y = y || 0;
        w = w || this.canvas.width;
        lh = lh || 1;
        method = method || 'fill';

        // manual linebreaks
        textParts = str.split('\n');
        textPartsNo = textParts.length;

        // split the words of the parts
        for (var i = 0; i < textParts.length; i++) {
            words[i] = textParts[i].split(' ');
        }

        // now that we have extracted the words
        // we reset the textParts
        textParts = [];

        // calculate recommended line breaks
        // split between the words
        for (var i = 0; i < textPartsNo; i++) {

            // clear the testline for the next manually broken line
            currLine = '';

            for (var j = 0; j < words[i].length; j++) {
                testLine = currLine + words[i][j] + ' ';

                // check if the testLine is of good width
                if (this.measureText(testLine).width > w && j > 0) {
                    textParts.push(currLine);
                    currLine = words[i][j] + ' ';
                } else {
                    currLine = testLine;
                }
            }
            // replace is to remove trailing whitespace
            textParts.push(currLine);
        }

        // render the text on the canvas
        for (var i = 0; i < textParts.length; i++) {
            if (method === 'fill') {
                this.fillText(textParts[i].replace(/((\s*\S+)*)\s*/, '$1'), x, y + (textSize * lh * i));
            } else if (method === 'stroke') {
                this.strokeText(textParts[i].replace(/((\s*\S+)*)\s*/, '$1'), x, y + (textSize * lh * i));
            } else if (method === 'none') {
                return { 'textParts': textParts, 'textHeight': textSize * lh * textParts.length };
            } else {
                console.warn('drawBreakingText: ' + method + 'Text() does not exist');
                return false;
            }
        }

        return { 'textParts': textParts, 'textHeight': textSize * lh * textParts.length };
    };
})(window, document);





var canvas = document.createElement('canvas');
var canvasWrapper = document.getElementById('canvasWrapper');
canvasWrapper.appendChild(canvas);
canvas.width = 500;
canvas.height = 500;
var ctx = canvas.getContext('2d');
var padding = 15;
var textTop = 'I use coding torque to learn';
var textBottom = 'web development by creating projects';
var textSizeTop = 10;
var textSizeBottom = 10;
var image = document.createElement('img');





image.onload = function (ev) {
    // delete and recreate canvas do untaint it
    canvas.outerHTML = '';
    canvas = document.createElement('canvas');
    canvasWrapper.appendChild(canvas);
    ctx = canvas.getContext('2d');
    document.getElementById('trueSize').click();
    document.getElementById('trueSize').click();

    draw();
};

document.getElementById('imgURL').oninput = function (ev) {
    image.src = this.value;
};

document.getElementById('imgFile').onchange = function (ev) {
    var reader = new FileReader();
    reader.onload = function (ev) {
        image.src = reader.result;
    };
    reader.readAsDataURL(this.files[0]);
};



document.getElementById('textTop').oninput = function (ev) {
    textTop = this.value;
    draw();
};

document.getElementById('textBottom').oninput = function (ev) {
    textBottom = this.value;
    draw();
};



document.getElementById('textSizeTop').oninput = function (ev) {
    textSizeTop = parseInt(this.value);
    draw();
    document.getElementById('textSizeTopOut').innerHTML = this.value;
};
document.getElementById('textSizeBottom').oninput = function (ev) {
    textSizeBottom = parseInt(this.value);
    draw();
    document.getElementById('textSizeBottomOut').innerHTML = this.value;
};



document.getElementById('trueSize').onchange = function (ev) {
    if (document.getElementById('trueSize').checked) {
        canvas.classList.remove('fullwidth');
    } else {
        canvas.classList.add('fullwidth');
    }
};



document.getElementById('export').onclick = function () {
    var img = canvas.toDataURL('image/png');
    var link = document.createElement("a");
    link.download = 'My Meme';
    link.href = img;
    link.click();

    var win = window.open('', '_blank');
    win.document.write('<img style="box-shadow: 0 0 1em 0 dimgrey;" src="' + img + '"/>');
    win.document.write('<h1 style="font-family: Helvetica; font-weight: 300">Right Click > Save As<h1>');
    win.document.body.style.padding = '1em';
};





function style(font, size, align, base) {
    ctx.font = size + 'px ' + font;
    ctx.textAlign = align;
    ctx.textBaseline = base;
}

function draw() {
    // uppercase the text
    var top = textTop.toUpperCase();
    var bottom = textBottom.toUpperCase();

    // set appropriate canvas size
    canvas.width = image.width;
    canvas.height = image.height;

    // draw the image
    ctx.drawImage(image, 0, 0, canvas.width, canvas.height);

    // styles
    ctx.fillStyle = '#fff';
    ctx.strokeStyle = '#000';
    ctx.lineWidth = canvas.width * 0.004;

    var _textSizeTop = textSizeTop / 100 * canvas.width;
    var _textSizeBottom = textSizeBottom / 100 * canvas.width;

    // draw top text
    style('Impact', _textSizeTop, 'center', 'bottom');
    ctx.drawBreakingText(top, canvas.width / 2, _textSizeTop + padding, null, 1, 'fill');
    ctx.drawBreakingText(top, canvas.width / 2, _textSizeTop + padding, null, 1, 'stroke');

    // draw bottom text
    style('Impact', _textSizeBottom, 'center', 'top');
    var height = ctx.drawBreakingText(bottom, 0, 0, null, 1, 'none').textHeight;
    console.log(ctx.drawBreakingText(bottom, 0, 0, null, 1, 'none'));
    ctx.drawBreakingText(bottom, canvas.width / 2, canvas.height - padding - height, null, 1, 'fill');
    ctx.drawBreakingText(bottom, canvas.width / 2, canvas.height - padding - height, null, 1, 'stroke');
}





image.src = 'https://imgflip.com/s/meme/The-Most-Interesting-Man-In-The-World.jpg';
document.getElementById('textSizeTop').value = textSizeTop;
document.getElementById('textSizeBottom').value = textSizeBottom;
document.getElementById('textSizeTopOut').innerHTML = textSizeTop;
document.getElementById('textSizeBottomOut').innerHTML = textSizeBottom;

Final Output

Meme Generator using JavaScript

Written by: Piyush Patil

If you found any mistakes or have any doubts please feel free to Contact Us

Hope you find this post helpful💖

Share your love