Disclaimers
This is a framework for a CAPTCHA. It only tells you if the player passed or failed the test. If you plan on using this for your game, you have to implement the punishment/blocking system yourself!
This is an image-based CAPTCHA, meaning it can be bypassed with an image recognition AI. The goal of this CAPTCHA is to deter inferior bots/AFK-farming and make it significantly harder for advanced bots to sneak in.
Hello roblox people, this is a CAPTCHA I’ve designed over the past week. I call it BubbleCAPTCHA because the testing image resembles a big group of bubbles.
Features
Procedurally generated random images
All the images you see are randomly generated. This means there’s no AssetId linked to anything and the test cannot be bypassed using a simple library of samples.
Purely mathematical
The images are made using vector math so the generating algorithm is decently performant. On my PC it takes less than a millisecond to generate an image.
Simple to solve
It only takes a total of two clicks to solve the CAPTCHA. One to reveal the image, one to choose the answer. It’s possible to modify the system to exchange accessibility for security and use a TextBox instead.
Exploit-resistant*
The only information revealed to the client to start the test is the image itself (stored as buffers) and a table of choices that the client picks. The direct answer to the test is never sent. Although it’s possible for bots to guess the answer from the choices, it’s exceedingly rare to get it right a few times in a row.
* It’s possible for exploiters to examine the serverside code (this is opensource) used to scramble the answer choices. They can predict where the answer will be based on the randomseed you used to randomly scramble the table of choices. Thus, it’s important that you scramble with care and keep your randomseed a secret.
As of now, this entire system is more of a proof of concept that can be perfected, but it is usable as-is in production.
It has a very simple API to challenge players and get the result:
API
Challenge.Challenge(
p: Player,
callBack: (UserId: number, pass: boolean, failReason: FailReason?) -> (),
random: Random,
duration: number?,
minTime: number?,
)
p: The player to challenge.
callBack: Function to call when the test is concluded. Sends to the function the UserId of the player, whether they passed, and the reason why they failed if they did.
random: The Random instance used for generating the image.
duration: Time allowed for the player to answer. The test automatically fails when the time is up.
minTime: Optional, the player must answer after this amount of seconds to not get disqualified.
FailReason
TimedOut - The player did not answer the challenge in time. Most common bot response.
Incorrect - The player chose the wrong answer.
TooFast - The player answered too fast.
AlreadyAnswered - A bug happened that let the player answer the same challenge twice.
BadData - (not used) The player responded to the challenge with something other than an answer.
Download
The framework was last updated on June 29th, 2024. The live demo and GitHub are up to date, but the direct rbxl download is for the outdated first version.
You can try out the CAPTCHA immediately here. The place is uncopylocked.
And the source code is available on GitHub:
If you prefer/want to see the first version of this CAPTCHA, a direct download is available:
BubbleCAPTCHA.rbxl (78.5 KB)
Honorable Mentions
Thanks to @EnumBobbert for inspiring me to create this.