ASCII-CAPTCHA(1)

NAME

ASCII-CaptchaConfig-first ASCII -> PNG CAPTCHA generator in Python. JSON-driven settings, CLI + importable API, deterministic…

SYNOPSIS

$pip install Pillow

INFO

25 stars
0 views

DESCRIPTION

Config-first ASCII -> PNG CAPTCHA generator in Python. JSON-driven settings, CLI + importable API, deterministic seeding, FastAPI example.

README

ASCII CAPTCHA

Config-first ASCII → image CAPTCHA generator. Single-file Python + Pillow. Works as a CLI or a library, with deterministic seeding for tests/CI.

Example CAPTCHA preview

Example preview image — output will vary unless you set random_seed or fixed_code.

  • Single-file, developer-friendly (simple to customize)
  • Fully config.json driven (fonts, noise, palette, seed, output)
  • Importable API + CLI
  • Deterministic mode via random_seed

Quick start

pip install Pillow
python captcha.py -c config.json
# (classic still works) python captcha.py -o out.png --print-code

Configuration

Everything lives in config.json. Drop this next to captcha.py:

{
  "output": "out/captcha.png",
  "print_code": true,
  "data_url_output": "out/captcha_data_url.txt",

"code_length": 6, "random_seed": 12345, "fixed_code": null,

"ascii_chars": "$@B%8&WM# ",

"text_to_ascii": { "font_path": null, "font_size": 40, "scale": 2 },

"render": { "font_path": null, "font_size": 9, "spacing": 1, "noise_lines": 24, "blur_shapes": 40, "apply_blur": true, "extra_noise_shapes": 30, "pixel_noise_density": 0.04, "jitter": 1, "gaussian_blur_radius": 1.1, "shape_blur_radius": 2.0 } }

Notes (plain English):

  • random_seed → lock output for tests (remove in prod)
  • fixed_code → force a known CAPTCHA (debug only)
  • ascii_charsDARK → LIGHT mapping
  • render.font_sizeDO NOT USE MORE THAN ~20 or readability drops
  • More noise = harder for bots (and heavier)

Use in Python

from captcha import generate_image, to_data_url

img, code = generate_image(length=6, seed=42) img.save("captcha.png") print("solution:", code)

handy for <img src="...">

data_url = to_data_url(img)

FastAPI demo

import io
from fastapi import FastAPI, HTTPException
from fastapi.responses import StreamingResponse
from captcha import generate_image

app = FastAPI() store = {} # replace with Redis/DB in production

@app.get("/captcha.png") def new_captcha(): img, code = generate_image(length=6) store["demo"] = code # bind to session/request id in real apps buf = io.BytesIO() img.save(buf, format="PNG"); buf.seek(0) return StreamingResponse(buf, media_type="image/png")

@app.post("/verify") def verify(code: str): if not store.get("demo"): raise HTTPException(400, "No captcha in store") ok = code.strip().upper() == store["demo"].upper() store.pop("demo", None) # one-time use return {"ok": ok}


Tips

  • Prefer a monospace font_path; keep render.spacing = 1
  • If the image is too messy: lower pixel_noise_density / blur_shapes
  • If too clean: increase noise_lines / extra_noise_shapes
  • Pair with rate limiting + session binding + short expiry

License & Contributing

MIT.

SEE ALSO

clihub4/9/2026ASCII-CAPTCHA(1)