Creating a Math CAPTCHA in JavaScript

Captchas are a vital part of web applications, providing a simple way to protect against bots and spam. In this post, we’ll walk you through building a math-based CAPTCHA using JavaScript, which generates an image with a simple math question for the user to solve.

Why Use a Math CAPTCHA?

A math CAPTCHA offers several advantages:

  • Simplicity: It’s easy for humans to solve but challenging for bots.

  • Accessibility: Math questions are straightforward and do not rely on visual or auditory recognition.

  • Customizable: You can adjust the difficulty level by changing the range of numbers.

The HTML Structure

Here’s the basic HTML structure for the CAPTCHA system. We use a <canvas> element to display the CAPTCHA image and input fields for the user to provide their answer.

<!DOCTYPE html>
<html>
<head>
    <style>
        .captcha-box {
            display: inline-block;
            text-align: center;
        }
        #captcha-image {
            margin-bottom: 10px;
            border: 1px solid #ccc;
        }
        #captcha-answer {
            width: 120px;
            text-align: center;
            margin-top: 5px;
        }
        .cbutton {
            margin-top: 5px;
            cursor: pointer;
            border: 1px solid;
            border-radius: 3px;
            background: #000000c7;
            color: white;
        }
        .cbutton:hover {
            background: #333333;
            color: white;
        }
        .cbutton:focus {
            outline: none;
            background: #444444;
            color: white;
        }
    </style>
</head>
<body>
    <div class="captcha-box">
        <canvas id="captcha-image" width="200" height="80"></canvas>
        <div class="row">
            <input type="number" id="captcha-answer" class="form-control" placeholder="Answer">
            <button type="button" onclick="verifyCaptcha()">Verify</button>
            <button type="button" onclick="generateCaptcha()" class="cbutton">Refresh</button>
        </div>
        <div id="captcha-result"></div>
    </div>
</body>
</html>

JavaScript: The Core Logic

The JavaScript code is responsible for generating the math question, rendering it on the canvas, and verifying the user’s input.

Step 1: Generating a Math Question

We generate two random numbers and calculate their sum. This sum is stored for later verification. To make the CAPTCHA harder for bots to solve, we add random noise and lines to the canvas.

        let correctAnswer;

        function generateCaptcha() {
            const canvas = document.getElementById('captcha-image');
            const ctx = canvas.getContext('2d');

            // Clear previous captcha
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            // Generate random numbers
            const num1 = Math.floor(Math.random() * 100) + 1;
            const num2 = Math.floor(Math.random() * 10) + 1;
            correctAnswer = num1 + num2;

            // Set background
            ctx.fillStyle = '#f0f0f0';
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            // Add noise (random dots)
            for (let i = 0; i < 100; i++) {
                ctx.beginPath();
                ctx.fillStyle = `rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255}, 0.2)`;
                ctx.arc(
                    Math.random() * canvas.width, 
                    Math.random() * canvas.height, 
                    Math.random(), 
                    0, 
                    Math.PI * 2
                );
                ctx.fill();
            }

            // Add lines
            for (let i = 0; i < 5; i++) {
                ctx.beginPath();
                ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height);
                ctx.lineTo(Math.random() * canvas.width, Math.random() * canvas.height);
                ctx.strokeStyle = `rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255}, 0.2)`;
                ctx.stroke();
            }

            // Draw text
            ctx.font = 'bold 30px Arial';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.fillStyle = 'black';
            ctx.fillText(`${num1} + ${num2} = ?`, canvas.width / 2, canvas.height / 2);

            // Reset answer and result
            document.getElementById("captcha-answer").value = "";
            document.getElementById("captcha-result").innerHTML = "";
        }

Step 2: Verifying User Input

The user’s input is compared to the precomputed answer. If it matches, the CAPTCHA is verified.

function verifyCaptcha() {
    const userAnswer = parseInt(document.getElementById("captcha-answer").value, 10);

    if (userAnswer === correctAnswer) {
        document.getElementById("captcha-result").style.color = "green";
        document.getElementById("captcha-result").innerHTML = "Verified";
    } else {
        document.getElementById("captcha-result").style.color = "red";
        document.getElementById("captcha-result").innerHTML = "Incorrect Captcha, try again!";
    }
}

// Initialize CAPTCHA on page load
window.onload = generateCaptcha;

Full Code Example

Here’s the complete HTML, CSS, and JavaScript code for the math CAPTCHA. You can copy and paste this into a single file to try it out:

<!DOCTYPE html>
<html>
<head>
    <style>
        .captcha-box {
            display: inline-block;
            text-align: center;
        }
        #captcha-image {
            margin-bottom: 10px;
            border: 1px solid #ccc;
        }
        #captcha-answer {
            width: 120px;
            text-align: center;
            margin-top: 5px;
        }
        .cbutton {
            margin-top: 5px;
            cursor: pointer;
            border: 1px solid;
            border-radius: 3px;
            background: #000000c7;
            color: white;
        }
        .cbutton:hover {
            background: #333333;
            color: white;
        }
        .cbutton:focus {
            outline: none; 
            background: #444444;
            color: white; 
        }
    </style>
</head>
<body>
    <div class="captcha-box">
        <canvas id="captcha-image" width="200" height="80"></canvas>
        <div class="row">
            <input type="number" id="captcha-answer" class="form-control" placeholder="Answer">
            <button type="button" onclick="verifyCaptcha()">Verify</button>
            <button type="button" onclick="generateCaptcha()" class="cbutton">Refresh</button>
        </div>
        <div id="captcha-result"></div>
    </div>

    <script>
        let correctAnswer;

        function generateCaptcha() {
            const canvas = document.getElementById('captcha-image');
            const ctx = canvas.getContext('2d');

            // Clear previous captcha
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            // Generate random numbers
            const num1 = Math.floor(Math.random() * 100) + 1;
            const num2 = Math.floor(Math.random() * 10) + 1;
            correctAnswer = num1 + num2;

            // Set background
            ctx.fillStyle = '#f0f0f0';
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            // Add noise (random dots)
            for (let i = 0; i < 100; i++) {
                ctx.beginPath();
                ctx.fillStyle = `rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255}, 0.2)`;
                ctx.arc(
                    Math.random() * canvas.width, 
                    Math.random() * canvas.height, 
                    Math.random(), 
                    0, 
                    Math.PI * 2
                );
                ctx.fill();
            }

            // Add lines
            for (let i = 0; i < 5; i++) {
                ctx.beginPath();
                ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height);
                ctx.lineTo(Math.random() * canvas.width, Math.random() * canvas.height);
                ctx.strokeStyle = `rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255}, 0.2)`;
                ctx.stroke();
            }

            // Draw text
            ctx.font = 'bold 30px Arial';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.fillStyle = 'black';
            ctx.fillText(`${num1} + ${num2} = ?`, canvas.width / 2, canvas.height / 2);

            // Reset answer and result
            document.getElementById("captcha-answer").value = "";
            document.getElementById("captcha-result").innerHTML = "";
        }

        function verifyCaptcha() {
            const userAnswer = parseInt(document.getElementById("captcha-answer").value, 10);

            if (userAnswer === correctAnswer) {
                document.getElementById("captcha-result").style.color = "green";
                document.getElementById("captcha-result").innerHTML = "Verified";
            } else {
                document.getElementById("captcha-result").style.color = "red";
                document.getElementById("captcha-result").innerHTML = "Incorrect Captcha, try again!";
            }
        }

        // Initialize CAPTCHA on page load
        window.onload = generateCaptcha;
    </script>
</body>
</html>

Key Features of This CAPTCHA

  1. Random Math Questions: Ensures uniqueness for every CAPTCHA.

  2. Noise and Lines: Adds a layer of difficulty for bots.

  3. Canvas Rendering: Makes it harder for bots to extract text programmatically.

Try It Out!

Copy the HTML and JavaScript code into your project, and you’ll have a fully functional CAPTCHA system ready to go.

By implementing this math CAPTCHA, you’ll add an extra layer of security to your application while keeping it user-friendly.