/ WEB APP

Set Apart Pixel

A portfolio that makes bots play games, and lose.

Next.js 16ReactTailwind CSSFramer Motion 12Firebase Adminjose
Set Apart Pixel interface
0
Spam messages
0
Lighthouse score
0
First load
0
Defence layers

01 / CONTEXT

Overview

THE CHALLENGE

Portfolio contact forms attract relentless spam. Standard CAPTCHAs (distorted text, traffic-light grids) are either solved by bots trivially or so frustrating that real visitors abandon the form. Neither option was acceptable for a design studio trying to make a strong first impression.

THE SOLUTION

Three defence layers work together. A silent honeypot field returns 200 OK to bots without recording anything, they think they succeeded. A server-side rate limiter (5 requests per 15 minutes per IP) blocks persistent automated attacks. Then two custom mini-games: the visitor must beat a Tic-Tac-Toe AI, then name an animal, country, and food starting with a random letter in 60 seconds, validated against a dictionary API. Spam keyword detection and disposable-email blocking filter anything that slips through.

THE OUTCOME

Spam dropped to near zero from day one. Genuine visitors consistently remark on the captcha as a memorable, on-brand experience, it demonstrates interactivity before they've even seen the portfolio.

02 / WHAT IT DOES

Key features

Silent Honeypot

A hidden form field invisible to real users. When a bot populates it and submits, the server returns 200 OK, the bot believes it succeeded and moves on. No CAPTCHA friction for real visitors at this stage.

IP Rate Limiting

Server-side middleware enforces 5 contact form submissions per 15 minutes per IP. Submissions beyond the limit are silently discarded. Implemented without an external service, pure Next.js server logic.

Tic-Tac-Toe Captcha

A minimax AI that makes one intentional suboptimal move per game, beatable with focus, not by a bot. Game state is validated server-side to prevent replay attacks.

Categories Game

A 60-second timed challenge with dictionary API validation and a spam keyword blocklist. Disposable email addresses (detected via domain pattern matching) are rejected before submission reaches Firestore.

Firestore Backend

Firebase Admin 13.7.0 writes validated contact submissions to Firestore. The Admin SDK keeps service account credentials server-side only, the client bundle has no Firebase config.

Framer Motion 12 Transitions

Page transitions and scroll-triggered reveals built with Framer Motion 12. Layout animations and viewport-triggered variants without heavy bundle overhead.

03 / STACK

Tech decisions

Tap any item to see why it earned a place.

04 / PROCESS

How it came together

05 / HARD PARTS

Challenges & solutions

React hydration runs after the browser paints, causing a visible flash when the stored theme differs from the HTML default. A blocking inline script in <head> reads localStorage and sets the data-theme attribute synchronously, before React mounts, before the first paint. No flash, no hydration mismatch.

Full minimax is unbeatable, which would frustrate real visitors and hand bots an unsolvable wall. A single intentional suboptimal move per game, chosen by weighted probability from a set of reasonable-looking moves, makes the AI feel skilled but human. Bots running random-move strategies still fail.

Visible spam defences (reCAPTCHA, error messages on the honeypot field) signal to bots what to avoid. The honeypot returns 200 OK silently; the rate limiter discards without an error code; keyword and domain checks happen server-side with no client feedback. Bots have no signal that anything is wrong.

LET'S BUILD SOMETHING TOGETHER

I'm always open to discussing new projects, partnerships, or just a good idea.