
How to Create an Image CAPTCHA Using Split Image Segments in JavaScript
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
- Displays an image split into 9 segments.
- Randomly selects a target object (e.g., road signal, car, zebra crossing).
- Users must select the correct segments.
- Validation message for success or failure.
- Refresh button to load a new image.
- 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>
- captcha-header: Displays the instruction (e.g., “Select the road signal”).
- captcha-container: Holds the grid of images.
- captcha-footer: Contains buttons for refresh, audio, validation message, and verification.
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;
}
- Ensures the CAPTCHA is visually appealing and user-friendly.
- Adds a green border when an image segment is selected.
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
- Image Selection: Randomly picks an image and ensures it’s not repeated consecutively.
- Grid Creation: Splits the image into 9 segments using background positioning.
- Selection Mechanism: Allows users to click on image parts to mark them.
- Verification: Compares the selected segments with the correct ones.
- Audio Prompt: Speaks out the instruction when the user clicks the voice button.
- 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!