A powerful and highly customizable CAPTCHA generator for Node.js applications, built on top of the fast skia-canvas
rendering engine. Generate secure, visually complex CAPTCHAs that are difficult for bots to solve while remaining accessible to humans.
- Features
- Installation
- Quick Start
- API Guide
- Configuration Options
- Examples
- Best Practices
- Troubleshooting
- API Reference
- Contributing
- License
- 🎨 Highly Customizable: Control every aspect including text, fonts, colors, backgrounds, rotation, skewing, and opacity
- 🔒 Security Focused: Built-in trace lines, decoy characters, and visual distortions to prevent OCR attacks
- ⚡ Dual API Design: Simple functions for quick generation or advanced classes for complex scenarios
- 🖼️ Background Support: Add custom background images for enhanced visual complexity
- 📱 Flexible Text: Support for multi-styled text segments and array-based configurations
- 🚀 Performance: Async/sync generation methods with efficient canvas rendering
- 📦 Lightweight: Minimal dependencies with peer dependency architecture
- 🎯 TypeScript Ready: Full TypeScript support with comprehensive type definitions
First, install the peer dependency skia-canvas
:
npm install skia-canvas
npm install captcha-canvas
- Node.js 14+
- Platform-specific requirements for
skia-canvas
(see skia-canvas documentation)
const fs = require("fs");
const { createCaptchaSync } = require("captcha-canvas");
// Generate a 300x100 captcha
const { image, text } = createCaptchaSync(300, 100);
fs.writeFileSync("captcha.png", image);
console.log("Captcha text:", text);
const { createCaptcha } = require("captcha-canvas");
async function generateCaptcha() {
const { image, text } = createCaptcha(300, 100);
const buffer = await image;
fs.writeFileSync("captcha.png", buffer);
console.log("Captcha text:", text);
}
Perfect for quick CAPTCHA generation with minimal configuration.
Generates a CAPTCHA asynchronously.
const { createCaptcha } = require("captcha-canvas");
const { image, text } = createCaptcha(400, 150, {
captcha: {
text: "HELLO",
size: 60,
color: "#2563eb"
},
decoy: {
total: 30,
opacity: 0.6
},
trace: {
color: "#dc2626",
size: 4
}
});
Generates a CAPTCHA synchronously (faster, but no background image support).
const { createCaptchaSync } = require("captcha-canvas");
const { image, text } = createCaptchaSync(400, 150, {
captcha: {
characters: 8,
size: 50,
color: "#059669"
}
});
Provides a fluent interface for advanced CAPTCHA customization with method chaining.
const { CaptchaGenerator } = require("captcha-canvas");
const captcha = new CaptchaGenerator()
.setDimension(200, 500)
.setCaptcha({
text: "SECURE123",
font: "Arial",
size: 60,
color: "#1f2937"
})
.setTrace({
color: "#ef4444",
size: 3
})
.setDecoy({
total: 40,
opacity: 0.7
});
// Generate synchronously
const buffer = captcha.generateSync();
fs.writeFileSync("captcha.png", buffer);
// Or generate asynchronously (required for background images)
const asyncBuffer = await captcha.generate();
// Complete customization
const captcha = new CaptchaGenerator()
.setDimension(250, 600)
.setBackground("./background.jpg")
.setCaptcha({
text: "CUSTOM",
font: "Comic Sans MS",
size: 70,
color: "#7c3aed",
skew: true,
rotate: 15,
opacity: 0.9
})
.setTrace({
color: "#dc2626",
size: 5,
opacity: 0.8
})
.setDecoy({
color: "#6b7280",
font: "Arial",
size: 30,
opacity: 0.4,
total: 50
});
const buffer = await captcha.generate();
Create CAPTCHAs with different styles for each character segment:
const captcha = new CaptchaGenerator()
.setDimension(200, 400)
.setCaptcha([
{ text: "AB", color: "red", size: 60, font: "Arial" },
{ text: "CD", color: "blue", size: 60, font: "Helvetica" },
{ text: "EF", color: "green", size: 60, font: "Times" }
]);
console.log(captcha.text); // "ABCDEF"
Low-level class for fine-grained control over the rendering process.
const { Captcha } = require("captcha-canvas");
const captcha = new Captcha(600, 200, 8); // width, height, characters
// Build the captcha step by step
captcha.addDecoy({ total: 20, size: 40 });
captcha.drawCaptcha({ size: 50, color: "#1e40af" });
captcha.drawTrace({ color: "#dc2626", size: 4 });
captcha.addDecoy({ opacity: 0.3 }); // Add more decoys on top
// Get the result
const buffer = captcha.png;
console.log("Generated text:", captcha.text);
{
text: "CUSTOM", // Custom text (overrides characters)
characters: 6, // Number of random characters to generate
font: "Arial", // Font family
size: 60, // Font size in pixels
color: "#000000", // Text color (hex, rgb, or named)
colors: ["#ff0000", "#00ff00"], // Array of colors (overrides color)
skew: true, // Apply random skewing
rotate: 15, // Max rotation angle in degrees
opacity: 0.8 // Text opacity (0-1)
}
{
color: "#ff0000", // Line color
size: 3, // Line thickness
opacity: 1.0 // Line opacity (0-1)
}
{
color: "#cccccc", // Decoy color
font: "Arial", // Decoy font
size: 20, // Decoy size
opacity: 0.5, // Decoy opacity (0-1)
total: 30 // Number of decoy characters
}
captcha.setDimension(height, width);
// Example: captcha.setDimension(150, 400);
const express = require('express');
const { CaptchaGenerator } = require('captcha-canvas');
const app = express();
app.get('/captcha', async (req, res) => {
const captcha = new CaptchaGenerator()
.setDimension(100, 300)
.setCaptcha({ characters: 6 });
const buffer = captcha.generateSync();
// Store captcha.text in session for verification
req.session.captcha = captcha.text;
res.setHeader('Content-Type', 'image/png');
res.send(buffer);
});
app.post('/verify', (req, res) => {
const userInput = req.body.captcha;
const sessionCaptcha = req.session.captcha;
if (userInput === sessionCaptcha) {
res.json({ success: true });
} else {
res.json({ success: false });
}
});
const captcha = new CaptchaGenerator()
.setDimension(120, 350)
.setCaptcha({
characters: 5,
font: "Impact",
size: 70,
color: "#ffffff",
skew: true,
rotate: 20
})
.setTrace({
color: "#ffffff",
size: 2,
opacity: 0.8
})
.setDecoy({
color: "#666666",
size: 25,
opacity: 0.3,
total: 20
});
const captcha = new CaptchaGenerator()
.setDimension(150, 400)
.setCaptcha({
characters: 6,
font: "Comic Sans MS",
size: 60,
colors: ["#ff6b6b", "#4ecdc4", "#45b7d1", "#96ceb4", "#ffeaa7"],
skew: true,
rotate: 25
})
.setTrace({
color: "#2d3436",
size: 3
});
const { resolveImage } = require('captcha-canvas');
async function createBackgroundCaptcha() {
const backgroundImage = await resolveImage('./pattern.jpg');
const captcha = new CaptchaGenerator()
.setDimension(200, 500)
.setBackground('./pattern.jpg')
.setCaptcha({
text: "SECURE",
size: 80,
color: "#ffffff",
opacity: 0.9
});
return await captcha.generate();
}
const captcha = new CaptchaGenerator()
.setDimension(180, 500)
.setCaptcha([
{
text: "SEC",
color: "#e74c3c",
size: 70,
font: "Impact",
rotate: 10
},
{
text: "URE",
color: "#3498db",
size: 70,
font: "Arial Black",
rotate: -10
}
])
.setTrace({
color: "#2c3e50",
size: 4
})
.setDecoy({
total: 35,
opacity: 0.4
});
console.log(captcha.text); // "SECURE"
- Always validate server-side: Never trust client-side CAPTCHA validation
- Use session storage: Store the correct answer in server-side sessions
- Implement rate limiting: Prevent brute force attacks
- Add expiration: Set time limits for CAPTCHA validity
- Use HTTPS: Protect CAPTCHA images and validation requests
// Example with expiration
app.get('/captcha', (req, res) => {
const captcha = new CaptchaGenerator().setCaptcha({ characters: 6 });
const buffer = captcha.generateSync();
req.session.captcha = {
text: captcha.text,
expires: Date.now() + 300000 // 5 minutes
};
res.setHeader('Content-Type', 'image/png');
res.send(buffer);
});
app.post('/verify', (req, res) => {
const session = req.session.captcha;
if (!session || Date.now() > session.expires) {
return res.json({ success: false, error: 'CAPTCHA expired' });
}
const isValid = req.body.captcha === session.text;
delete req.session.captcha; // Clear after use
res.json({ success: isValid });
});
- Use sync generation when possible: Faster for simple CAPTCHAs without backgrounds
- Cache background images: Load background images once and reuse
- Optimize dimensions: Larger images take more processing time
- Limit decoy characters: Too many decoys can impact performance
- Provide audio alternatives: Consider implementing audio CAPTCHAs
- Use sufficient contrast: Ensure text is readable for visually impaired users
- Offer refresh options: Allow users to generate new CAPTCHAs
- Consider alternative verification: Implement fallback verification methods
# Install the peer dependency
npm install skia-canvas
This usually indicates a problem with skia-canvas installation. Try:
# Reinstall skia-canvas
npm uninstall skia-canvas
npm install skia-canvas
# On some systems, you might need:
npm install skia-canvas --build-from-source
Ensure you're using the async generate()
method:
// ❌ Wrong - sync method doesn't support backgrounds
captcha.setBackground('./bg.jpg');
const buffer = captcha.generateSync(); // Background ignored
// ✅ Correct - async method supports backgrounds
captcha.setBackground('./bg.jpg');
const buffer = await captcha.generate();
This error occurs when you specify both text
and characters
with mismatched lengths:
// ❌ Wrong - text length (5) doesn't match characters (6)
captcha.setCaptcha({
text: "HELLO",
characters: 6
});
// ✅ Correct - let the library handle character count
captcha.setCaptcha({
text: "HELLO"
});
// ✅ Or specify only character count for random text
captcha.setCaptcha({
characters: 6
});
- Ensure Visual Studio Build Tools are installed
- Use Node.js 14+ with npm 6+
- Install Xcode Command Line Tools:
xcode-select --install
- Install required system dependencies for skia-canvas
- See skia-canvas Linux requirements
For complete API documentation with all methods, properties, and type definitions, visit:
-
CaptchaGenerator
- High-level fluent interface -
Captcha
- Low-level canvas manipulation
-
createCaptcha(width, height, options?)
- Async simple generation -
createCaptchaSync(width, height, options?)
- Sync simple generation -
resolveImage(path)
- Load images for backgrounds
-
.setDimension(height, width)
- Set canvas size -
.setCaptcha(options)
- Configure text appearance -
.setTrace(options)
- Configure trace line -
.setDecoy(options)
- Configure decoy characters -
.setBackground(image)
- Set background image -
.generate()
- Generate async (supports backgrounds) -
.generateSync()
- Generate sync (faster, no backgrounds)
We welcome contributions! Here's how you can help:
# Clone the repository
git clone https://github.com/Shashank3736/captcha-canvas.git
cd captcha-canvas
# Install dependencies
npm install
# Build the project
npm run build
# Run examples
npm run example
# Generate documentation
npm run docsgen
- Fork the repository and create a feature branch
- Write tests for new functionality
- Follow TypeScript best practices and existing code style
- Update documentation for API changes
- Test across platforms when possible
- Submit a pull request with a clear description
When reporting bugs, please include:
- Node.js version
- Operating system
- captcha-canvas version
- Minimal reproduction code
- Error messages and stack traces
Open an issue at: GitHub Issues
This project is licensed under the Apache-2.0 License. See the LICENSE file for details.
Thanks goes to these wonderful people (emoji key):
Shashank 📆 |
TheDeadCraftPT 🐛 |
IchiiDev 🐛 |
Ikramullah 📖 💻 |
ryanhex53 💻 |
This project follows the all-contributors specification. Contributions of any kind welcome!