YMJake.FusionCaptcha.Core
1.1.1
dotnet add package YMJake.FusionCaptcha.Core --version 1.1.1
NuGet\Install-Package YMJake.FusionCaptcha.Core -Version 1.1.1
<PackageReference Include="YMJake.FusionCaptcha.Core" Version="1.1.1" />
<PackageVersion Include="YMJake.FusionCaptcha.Core" Version="1.1.1" />
<PackageReference Include="YMJake.FusionCaptcha.Core" />
paket add YMJake.FusionCaptcha.Core --version 1.1.1
#r "nuget: YMJake.FusionCaptcha.Core, 1.1.1"
#:package YMJake.FusionCaptcha.Core@1.1.1
#addin nuget:?package=YMJake.FusionCaptcha.Core&version=1.1.1
#tool nuget:?package=YMJake.FusionCaptcha.Core&version=1.1.1
FusionCaptcha
FusionCaptcha is a .NET captcha toolkit built on top of the Vello rendering pipeline. It focuses on three core responsibilities—code generation, image rendering, and persistence—while leaving fonts, backgrounds, and other resources entirely in the hands of your application. This keeps the library lightweight and easy to integrate into Web API, Minimal API, or background services.
Packages
| Package | Description |
|---|---|
YMJake.FusionCaptcha.Abstractions |
Core contracts (ICaptcha, ICaptchaRenderer, IStorage, CaptchaOptions, etc.). |
YMJake.FusionCaptcha.Core |
Default pipeline (background → text → noise → distortion), Vello renderer, animated GIF renderer, font/background providers, DI extensions. |
YMJake.FusionCaptcha.Storage.Memory |
IMemoryCache-based storage provider. |
YMJake.FusionCaptcha.Storage.Redis |
StackExchange.Redis-based storage provider with async APIs. |
💡 Sample Code: Check out the Minimal API sample in the repository for a complete working example with Swagger integration.
Installation
dotnet add package YMJake.FusionCaptcha.Core
dotnet add package YMJake.FusionCaptcha.Storage.Memory
# or
dotnet add package YMJake.FusionCaptcha.Storage.Redis
Vello’s native dependencies (
vello_ffi.dll, etc.) are shipped transitively via NuGet. Ensure your runtime RID is supported by Vello.
Quick Start (ASP.NET Core)
using YMJake.FusionCaptcha.Abstractions;
using YMJake.FusionCaptcha.Core;
using YMJake.FusionCaptcha.Storage.Memory;
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddCaptcha(options =>
{
options.Image.Width = 160;
options.Image.Height = 60;
options.Image.FontSize = 36;
options.Image.FontFamily = "ActionJ";
options.Image.BackgroundImage = "Grid";
})
.UseFileResources(resources =>
{
resources.Fonts.BasePath = Path.Combine(builder.Environment.ContentRootPath, "Resources", "Fonts");
resources.Fonts.AddFont("actionj.ttf", "ActionJ");
resources.Backgrounds.BasePath = Path.Combine(builder.Environment.ContentRootPath, "Resources", "Backgrounds");
resources.Backgrounds.AddBackground("grid.png", "Grid");
})
.UseVelloRenderer()
.UseCaptchaMemoryStorage();
var app = builder.Build();
app.MapGet("/captcha", async (string id, ICaptcha captcha) =>
{
var result = await captcha.GenerateAsync(id);
return Results.File(result.Bytes, result.ContentType);
});
app.MapGet("/captcha/validate", (string id, string code, ICaptcha captcha) =>
{
var success = captcha.Validate(id, code);
return Results.Ok(new { id, code, success });
});
app.Run();
Resource Management
- Fonts – Inject via
IFontProvider.UseFileResources/UseFileFontOptionsloads fonts from disk and exposes them throughoptions.Image.FontFamily. - Backgrounds – Provided by
IBackgroundProvider. The default is a solid color, but you can register multiple background images and pick one viaoptions.Image.BackgroundImage. - Pipeline Steps –
BackgroundStep,TextStep,NoiseStep,DistortionStepall implementICaptchaPipelineStep. You can replace or extend them with custom steps.
Storage Options
UseCaptchaMemoryStorage()– UsesIMemoryCache, ideal for single-node deployments or tests.UseCaptchaRedisStorage(options => { ... })– Backs captcha storage with Redis; configure connection string, database index, key prefix, etc.
Animated GIF Renderer
Need moving text/noise to highlight bots? Swap out the default PNG renderer with the GPU-accelerated GIF renderer:
builder.Services
.AddCaptcha(options =>
{
options.Image.Width = 160;
options.Image.Height = 60;
})
.UseFileResources(resources => { /* fonts / backgrounds */ })
.UseGifRenderer(gif =>
{
gif.FrameCount = 6; // default
gif.FrameDelay = 5; // 1/100th of a second
gif.TextJitter = 1.5f; // per-frame wobble in pixels
gif.NoiseJitter = 1.2f;
gif.SequentialFade = true; // reveal characters progressively
gif.TextAlphaMin = 0.35f; // fade range
gif.TextAlphaMax = 1f;
gif.NoiseAlpha = 0.7f; // keep lines subtle
gif.RepeatCount = 0; // 0=infinite, >0 fixed loops
})
.UseCaptchaMemoryStorage();
UseGifRenderer internally reuses the Vello renderer in raw RGBA mode so every frame is generated on the GPU first and then encoded into a GIF via ImageSharp. The resulting CaptchaRenderResult has ContentType = "image/gif" and isAnimated = true.
Bubbles & Noise Customization
FusionCaptcha mirrors LazyCaptcha's "bubble + curved noise" style via CaptchaImageOptions:
builder.Services.AddCaptcha(options =>
{
options.Image.Width = 160;
options.Image.Height = 60;
options.Image.BubbleCount = 4;
options.Image.BubbleMinRadius = 3;
options.Image.BubbleMaxRadius = 10;
options.Image.BubbleOpacityMin = 0.2f;
options.Image.BubbleOpacityMax = 0.45f;
options.Image.InterferenceLineCount = 2;
options.Image.InterferenceLineSegments = 3; // split straight line into segments
options.Image.InterferenceLineCurvature = 8f;
options.Image.NoiseThickness = 1.2f;
});
Bubble shapes render after the background step and before text, using random alpha per bubble. InterferenceLineSegments/InterferenceLineCurvature split noise lines into multiple segments so they resemble Bezier curves.
Captcha Types
Pick any of the 12 built-in captcha types (aligned with LazyCaptcha) by setting options.CaptchaType:
CaptchaType |
Rendered Text | Validation Value | Notes |
|---|---|---|---|
Default |
Mixed letters + digits | Same as rendered | Common combo |
Chinese |
Random Chinese characters | Same as rendered | Uses built-in Hanzi list |
Number |
Digits | Same as rendered | Numeric only |
NumberZhCn |
Chinese numerals (lowercase) | Matching digit | Render Chinese, validate number |
NumberZhHk |
Chinese numerals (uppercase) | Matching digit | Render Chinese, validate number |
Word |
Letters (upper + lower) | Same as rendered | No digits |
WordLower |
Lowercase letters | Same as rendered | No digits |
WordUpper |
Uppercase letters | Same as rendered | No digits |
WordNumberLower |
Lowercase letters + digits | Same as rendered | Matches Lazy's mode |
WordNumberUpper |
Uppercase letters + digits | Same as rendered | Matches Lazy's mode |
Arithmetic |
Expressions like 3+5, 9-4 |
Computed result | Auto-adjust addition/subtraction |
ArithmeticZh |
Chinese expressions | Computed result | Render Chinese, return numbers |
Arithmetic modes clamp results to keep expressions easy to solve.
Slide Captcha (Beta)
Install YMJake.FusionCaptcha.SlideCaptcha to get a puzzle-style slider captcha:
using YMJake.FusionCaptcha.SlideCaptcha;
builder.Services
.AddSlideCaptcha(options =>
{
options.Width = 310;
options.Height = 155;
options.Tolerance = 0.03;
options.InterferenceCount = 1;
options.BackgroundImage = "Grid"; // optional, resolved via IBackgroundProvider
})
.UseFileBackgrounds(backgrounds =>
{
backgrounds.BasePath = Path.Combine(builder.Environment.ContentRootPath, "Resources", "Backgrounds");
backgrounds.AddBackground("flowers.png", "Grid");
})
.UseCaptchaMemoryStorage();
app.MapGet("/slide", (ISlideCaptcha captcha) => captcha.Generate());
app.MapPost("/slide/validate", (string id, SlideTrack track, ISlideCaptcha captcha) => captcha.Validate(id, track));
- Backgrounds reuse your existing
UseFileResources/UseBackgroundProviderconfiguration; otherwise a solid color fallback is used. - Backgrounds can reuse existing
UseFileResourcesor be configured per-slide via.UseFileBackgrounds(...). - Slider templates default to embedded resources but can be replaced by custom providers.
- Validation expects a
SlideTrackpayload (drag trajectory + timings) and returnsValidateResultwith success/fail/timeout.
Click Selection Captcha (Beta)
Install YMJake.FusionCaptcha.ClickSelectionCaptcha to add a tap-to-select experience similar to Lazy's point captcha. It reuses your background/font providers and stores session state via IStorage.
using YMJake.FusionCaptcha.ClickSelectionCaptcha;
builder.Services
.AddClickSelectionCaptcha(options =>
{
options.Width = 320;
options.Height = 200;
options.TargetCount = 4;
options.NoiseCount = 4;
options.FontFamily = "SourceHanSans";
options.BackgroundImage = "Back";
})
.UseFileResources(resources =>
{
resources.Fonts.BasePath = Path.Combine(builder.Environment.ContentRootPath, "Resources", "Fonts");
resources.Fonts.AddFont("SourceHanSansCN-Regular.otf", "SourceHanSans");
resources.Backgrounds.BasePath = Path.Combine(builder.Environment.ContentRootPath, "Resources", "Backgrounds");
resources.Backgrounds.AddBackground("back.png", "Back");
})
.UseCaptchaMemoryStorage();
app.MapGet("/point", (IClickSelectionCaptcha captcha) => captcha.Generate());
app.MapPost("/point/validate", (string id, ClickSelectionCaptchaAnswer answer, IClickSelectionCaptcha captcha) => captcha.Validate(id, answer));
- The generator draws random characters on top of your chosen background and returns both the base64 image and the ordered instructions (e.g.,
["木","冬","景","川"]). - Validation expects clicks normalized to the original width/height;
ClickSelectionCaptchaAnsweralready mirrors what the Vue sample posts. UseFileResourcesworks the same as the core pipeline: fonts supply Chinese glyphs, backgrounds are reused from your main captcha configuration.
Samples
| Sample | Description |
|---|---|
samples/YMJake.FusionCaptcha.MinimalApi |
ASP.NET Core Minimal API exposing /captcha, /slide, and /point endpoints. Run with dotnet run --project samples/YMJake.FusionCaptcha.MinimalApi. |
samples/captcha.test |
Vue 3 + Vite playground that exercises the slide captcha and the click-selection captcha. Run npm install && npm run dev inside the folder, keep the Minimal API running on http://localhost:5222, and browse to http://localhost:5173. |
The Vue sample mirrors Lazy's demo UX: the slider component records drag trajectories, while ClickBasedCaptcha.vue lets you click the prompted characters in sequence and visualizes the submitted points. It is intentionally framework-agnostic so you can port the logic into your own frontend.
Roadmap
- Interactive captcha modes (slider puzzle, tap-to-select, trajectory, etc.) with behavior verification.
- Pluggable pipeline DSL similar to
QrBuilder. - GIF / WebP / ImageSharp encoders.
- Storage-level TTL strategies and hit stats for custom rate limiting.
Contributions and feature requests are always welcome!
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Options (>= 9.0.0)
- SixLabors.ImageSharp (>= 3.1.12)
- VelloSharp (>= 0.5.0-alpha.3)
- VelloSharp.Native.Vello.win-x64 (>= 0.5.0-alpha.3)
- VelloSharp.Native.VelloSparse (>= 0.5.0-alpha.3)
- VelloSharp.Text (>= 0.5.0-alpha.3)
- YMJake.FusionCaptcha.Abstractions (>= 1.1.1)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on YMJake.FusionCaptcha.Core:
| Package | Downloads |
|---|---|
|
YMJake.FusionCaptcha.Storage.Memory
In-memory storage provider for YMJake.FusionCaptcha. |
|
|
YMJake.FusionCaptcha.Storage.Redis
Redis storage provider for YMJake.FusionCaptcha. |
GitHub repositories
This package is not used by any popular GitHub repositories.