How to Create an Image CAPTCHA Using Split Image Segments in JavaScript
March 21, 20255 min read

How to Create an Image CAPTCHA Using Split Image Segments in JavaScript

Web developmentJavaScriptCSS3JavaScript Projects
share this article on
CAPTCHAs help prevent bots from interacting with websites by asking users to complete visual tasks. In this guide, we'll create an image CAPTCHA where users select specific segments of an image, such as identifying road signals within a split image.

What We’ll Build

We will display an image split into a 3x3 grid. Each segment corresponds to part of the image, and the user must correctly select the segments that contain a given object (e.g., a road signal, car, or zebra crossing). If the selection is correct, the CAPTCHA is validated; otherwise, a new image is loaded.

Features of the CAPTCHA

  1. Displays an image split into 9 segments.
  2. Randomly selects a target object (e.g., road signal, car, zebra crossing).
  3. Users must select the correct segments.
  4. Validation message for success or failure.
  5. Refresh button to load a new image.
  6. Audio prompt to guide users.

Step 1: Setting Up the HTML Structure

The HTML defines the basic structure of the CAPTCHA interface. Here’s the code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Image CAPTCHA with Split Image</title>
    <link rel="stylesheet" href="styles.css">
    
</head>
<body>
    <div class="captcha-card">
        <div class="captcha-header">
            Select the <strong>road signal</strong>
        </div>
        <div id="captcha-container" class="captcha-grid"></div>
        <div class="captcha-footer">
            <span class="icon" id="refresh-btn">🔄</span>
            <span class="icon" id="voice-btn">🔊</span>
            <div class="validation-message" id="validation-message"></div>
            <button id="verify-btn">Verify</button>
        </div>
    </div>
    <audio id="audio-instruction" src="" preload="auto"></audio>
    <script src="split-captcha.js"></script>
</body>
</html>

Step 2: CSS for Styling

body {
    font-family: Arial, sans-serif;
    background-color: #161a20;
    color: #fff;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
}
.captcha-card {
    background-color: #fff;
    color: #000;
    width: 400px;
    border-radius: 10px;
    overflow: hidden;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
    text-align: center;
}
.captcha-header {
    padding: 20px;
    background-color: #00b0ad;
    font-size: 16px;
    color: #fff;
}
.captcha-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 2px;
    background-color: #ddd;
}
.captcha-segment {
    width: 100%;
    aspect-ratio: 1;
    background-size: 300% 300%;
    cursor: pointer;
    transition: 0.3s;
}
.captcha-segment.selected {
    border: 2px solid green; /* Only a border change */
    opacity: 0.5;
}
.captcha-footer {
    padding: 18px 10px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.captcha-footer button {
    background-color: #00b0ad;
    border: none;
    padding: 10px;
    color: #fff;
    border-radius: 5px;
    cursor: pointer;
    font-size: 14px;
}
.captcha-footer button:hover {
    background-color: #05cecb;
}
.icon {
    cursor: pointer;
    font-size: 18px;
}
.validation-message {
    flex-grow: 1;
    text-align: center;
    color: #000;
    font-size: 14px;
}

Step 3: Adding Functionality with JavaScript

const captchaContainer = document.getElementById('captcha-container');
const verifyButton = document.getElementById('verify-btn');
const refreshButton = document.getElementById('refresh-btn');
const validationMessage = document.getElementById('validation-message');
const voiceButton = document.getElementById('voice-btn');

// Image and target configuration
const captchaConfig = [
    {
        src: './images/road-signal.png',
        target: 'road signal',
        correctSegments: [0, 2, 3, 5]
    },
    {
        src: './images/cars.jpg',
        target: 'car',
        correctSegments: [3, 4, 5, 7, 8]
    },
    {
        src: './images/zebra-crossing.jpg',
        target: 'zebra crossing',
        correctSegments: [6, 7, 8]
    }
];

let currentImage = null;
let previousImage = null;

function loadCaptcha() {
    captchaContainer.innerHTML = '';
    validationMessage.textContent = '';

    // Select a random image, ensuring it is not the same as the previous one
    let randomIndex;
    do {
        randomIndex = Math.floor(Math.random() * captchaConfig.length);
    } while (captchaConfig[randomIndex] === previousImage);

    currentImage = captchaConfig[randomIndex];
    previousImage = currentImage;

    // Update header text dynamically based on the selected image
    const header = document.querySelector('.captcha-header');
    header.innerHTML = `Select the <strong>${currentImage.target}</strong>`;

    // Generate grid segments
    const gridSize = 3; // 3x3 grid
    for (let i = 0; i < gridSize * gridSize; i++) {
        const segment = document.createElement('div');
        segment.classList.add('captcha-segment');

        // Set background image for the segment
        segment.style.backgroundImage = `url('${currentImage.src}')`;
        const x = -(i % gridSize) * 100;
        const y = -Math.floor(i / gridSize) * 100;
        segment.style.backgroundPosition = `${x}% ${y}%`;
        segment.style.backgroundSize = `300% 300%`;

        // Add click handler
        segment.addEventListener('click', () => {
            segment.classList.toggle('selected');
        });

        captchaContainer.appendChild(segment);
    }
}

function playAudio() {
    const message = `Select the ${currentImage.target}`;
    const speech = new SpeechSynthesisUtterance(message);
    speech.lang = "en-US";
    window.speechSynthesis.speak(speech);
}

function verifyCaptcha() {
    const selectedSegments = document.querySelectorAll('.captcha-segment.selected');
    const selectedIndexes = Array.from(selectedSegments).map(segment =>
        Array.from(captchaContainer.children).indexOf(segment)
    );

    const isCorrect =
        selectedIndexes.length === currentImage.correctSegments.length &&
        currentImage.correctSegments.every(index => selectedIndexes.includes(index));

    if (isCorrect) {
        validationMessage.textContent = '✅ Valid CAPTCHA!';
    } else {
        validationMessage.textContent = '❌ Invalid CAPTCHA! Refreshing...';
        setTimeout(() => {
            loadCaptcha(); // Ensure a new random image is loaded
        }, 1000);
    }
}

refreshButton.addEventListener('click', loadCaptcha);
voiceButton.addEventListener('click', playAudio);
verifyButton.addEventListener('click', verifyCaptcha);

// Initial captcha load
loadCaptcha();

Explanation

  1. Image Selection: Randomly picks an image and ensures it’s not repeated consecutively.
  2. Grid Creation: Splits the image into 9 segments using background positioning.
  3. Selection Mechanism: Allows users to click on image parts to mark them.
  4. Verification: Compares the selected segments with the correct ones.
  5. Audio Prompt: Speaks out the instruction when the user clicks the voice button.
  6. Refresh: Loads a new image when the user fails.

Conclusion

This CAPTCHA system effectively prevents bots by requiring users to visually recognize objects in split images. It offers an engaging, interactive, and secure way to verify human presence on websites. You can extend it by adding more images or using AI-based image recognition for better validation.

Try implementing this on your website to improve security and user experience!


Back to Articles