I build browser products and run the infrastructure behind them. Custom cursors, YouTube skins, multiplayer games and a multilingual chat up front; Cloudflare, k3s, Argo CD and the data services underneath.
One operator, from the first pixel to the production cluster.
|
cursor.style A small detail. A browser that feels like yours. |
YouTube Skins Give the player a different personality. |
|
Slither Multiplayer. No install. One more round. |
Terra A little open world to get lost in. In preview. |
Try the Cursor Playground ↗
Pick a pointer, draw a trail, and meet a little character that follows your mouse.
All products, audiences and links
| Product | What it is | Reach |
|---|---|---|
| cursor.style | The largest free cursor library, plus the extension that applies them | |
| cursor-land.com | Second cursor brand - same engine, its own audience | |
| youtube-skins.com | 130+ skins for the YouTube player, and an ad skipper beside it | |
| fb.zone | Video and photo background themes for Facebook | |
| Chat | Moderated rooms, direct messages, photo albums and WebRTC calls, built into the main site | rooms, DMs, group voice |
| Slither | Free multiplayer snake, no install, no account | live on the games hub |
| Terra | Open 3D world you can run around in, built on streamed low-poly terrain | in preview |
| agropost.com.ua | Ukrainian agricultural marketplace: listings, company catalogue, grain logistics | running since 2010 |
| agro-post.com | The same platform for the US market | live |
Chrome Web Store counts and ratings are provided by Shields.io and may be cached.
Combined traffic across all active domains accessible to the Cloudflare connection, including their proxied subdomains. Requests include automated traffic; these are not deduplicated visitors across sites.
368,782,497 requests · 15,143,713 page views · 4,480,760 unique visitors over the last 30 days
Yesterday alone: 6,894,204 requests from 392,827 people. Refreshed 2026-10-28 by the workflow above.
The chat grew out of the cursor library: rooms, direct messages, photo albums, group voice and WebRTC calls, in 49 languages. Every username, message and photo goes through moderation before anyone else sees it.
How the moderation pipeline works
Most of the people in it are teenagers. The hard part is judging content in languages I do not read, in under a second, at a cost per item of approximately nothing.
| Layer | What runs |
|---|---|
| Photos | OpenAI omni-moderation-latest, with per-category score thresholds measured against real uploads rather than guessed |
| A second opinion on photos | Amazon Rekognition, wired in beside it and running in shadow: it logs its labels and decides nothing until they prove the thresholds. The two miss different things - a real shotgun scored 0 at OpenAI and 100 at Rekognition, which is the whole argument for keeping both |
| Text | The same moderation model, then gpt-4o-mini on what scores zero and is still a problem: a phone number handed over in a direct message, or an intent that only reads as harmful in the language it was written in |
| Words | A two-level dictionary per language, because in some of them a slur is one space away from an ordinary phrase, and a naive filter bans half the room |
| Reports | Triaged automatically before a human opens them, with a ladder of sanctions that does not wait for one |
| Warnings | Treated as a product rather than a log line: a badge beside the avatar, a modal the account has to acknowledge, and a record of what was decided and when |
Failing open is the expensive direction here, so an outage at the moderator fails the upload instead of waving it through.
AgroPost has run since 2010: a marketplace, company catalogue and grain-logistics board for Ukrainian agriculture, now with a US edition at agro-post.com. It is the least glamorous thing I operate and the one that ended up in other people's teaching materials.
| Institution | Where it appears | Year |
|---|---|---|
| Odesa State Agrarian University | Course work programme, listing AgroPost as a notice board, company catalogue and trading platform | 2025 |
| Mykolaiv National Agrarian University | Methodical recommendations for crop production technology | 2021 |
| Mykolaiv National Agrarian University | Technical crops teaching material | 2016 |
| National University of Food Technologies | Conference proceedings, bibliography citing the elevators directory | 2015 |
Five public brands, one cluster, one operator. Nothing reaches production except through Git: GitHub Actions runs build jobs on GitHub-hosted runners or self-hosted runners in AWS CodeBuild or on EC2 Spot instances, then pushes the images and updates the Git manifests. Argo CD syncs those manifests, External Secrets pulls configuration out of AWS Parameter Store, and Cloudflare Zero Trust is the only door into the admin side. If it is not in a repository, it is not running.
A separate image-processing project uses API Gateway and Lambda to generate presigned upload URLs; browsers upload files directly to S3.
Moving packets illustrate the architecture; they are not a live traffic or availability monitor.
Eleven in total. The last three are set in this page's own style, because the issuers publish no badge artwork for them.
67 owned repositories, public and private; 10,473 all-time GitHub commit contributions (public + private); 10,639 all-time contributions (public + private).
Commit counts follow GitHub contribution rules; they are not a count of every commit on every branch. Languages reflect public and private, non-fork repositories. Snapshot: 2026-10-28 (UTC).
| Repository | What it does |
|---|---|
| laravel-gsc-manager | Google Search Console inside Laravel, authenticated with a service account |
| ai-php-json-files-language-translator | Translates PHP, JSON and plain-text language files with an LLM, keeping placeholders intact |
| tabler-icons-font-picker | Tabler icon picker with categories, search and a drop-in field |
| image-processing | Image pipeline on AWS: ECS, Lambda, Aurora and S3 |
| slither | The multiplayer snake running at cursor.style/games |
How this page builds itself
Nothing here is a screenshot, and no image is hand-placed pixel art.
assets/hero.svgis generated bytools/build_hero.py. GitHub serves README images underdefault-src 'none', so an SVG can never pull a web font down. Every piece of display type is therefore baked into<path>outlines from ASCII subsets of Baloo 2 and JetBrains Mono, kept intools/fonts/with their OFL licences. The mascot is embedded as a data URI for the same reason.assets/traffic.svgis redrawn every night.update_readme.mjsasks the Cloudflare GraphQL API for yesterday and for the last 30 days, writesassets/traffic.json, andtools/build_traffic.pyturns that into the chart above - bars, busiest-day marker, aligned figures and all.assets/github.svgandassets/github-mobile.svgshare one GitHub snapshot. The card shows owned repositories (including forks), all-time commit contributions, and all-time contributions. GitHub limits each query to one year, so the generator sums separate calendar-year windows internally. The language mix covers public and private non-fork repositories when the owner token has access; private repository details are never published. Both the image description and the readable summary update from that same snapshot.assets/portfolio.svgcombines a 30-day window across all active Cloudflare zones available to the token, with countries merged and cache percentage weighted by request count. It does not sum unique visitors or infer uptime or latency. A failed zone query or loss of previously available zones preserves the previous complete snapshot and its date.- Product previews and architecture are self-contained animated SVGs. They use the same outlined type as the other cards, support reduced motion, and run directly inside the GitHub profile without scripts.
- Cursor Playground is a small optional HTML/CSS/JavaScript experiment in
docs/, published on GitHub Pages. The profile itself stays fully readable on GitHub; the playground adds real pointer, touch and keyboard interaction on its own page. - The numbers in the text come from the same run, so the readable version stays correct even if images are blocked.
- The snake is redrawn from the contribution graph by a second workflow and published to the
outputbranch.
Rebuild the artwork locally with:
pip install fonttools pillow
python tools/build_hero.py
python tools/build_traffic.py
python tools/build_architecture.py
python tools/build_showcase.py
node tools/fetch_portfolio_stats.mjs
python tools/build_portfolio.py
GITHUB_TOKEN=... python tools/build_github_card.pyUse python tools/build_github_card.py --offline to redraw both GitHub cards from the committed data.
The combined-site collector uses CLOUDFLARE_API_TOKEN with Zone / Zone / Read and Zone / Analytics / Read for the intended domains. An optional CLOUDFLARE_PORTFOLIO_API_TOKEN secret overrides it for this collector only. It discovers active zones automatically, counts each zone once, and publishes only aggregate metrics and irreversible coverage fingerprints; domain names, zone IDs and account IDs are not stored. Run node tools/fetch_portfolio_stats.mjs --check to verify access without writing a snapshot.
For account-wide statistics, configure the PROFILE_STATS_TOKEN Actions secret with an owner token belonging to wonchoe (a classic personal access token or an OAuth token), with repo and read:user scopes. GitHub CLI can request the latter scope with gh auth refresh --hostname github.com --scopes read:user. The workflow's default GITHUB_TOKEN only has repository-level access. Repository totals and contribution totals have separate access requirements; a token can see private repositories while still returning only public contribution counts. Fine-grained or otherwise limited tokens are labelled as showing the data visible to that token, without claiming full account coverage.
Only aggregate counts are published: private repository names, code, commit messages and tokens are never saved in the snapshot. Commit totals follow GitHub's contribution rules (including eligible branches and linked author emails), and restricted contributions are never added to commit counts because they may also include issues or pull requests. On refresh failure or reduced access, the previous snapshot and its original date stay in place.








