First AI-assisted project

Nades

A fast Counter-Strike 2 utility reference built to make individual lineups—and coordinated executes—easy to find and follow.

Making utility knowledge usable in the moment

Nades began as a focused tool for tracking Counter-Strike 2 utility lineups. The goal was a site that stayed fast and simple while still representing the details that make a lineup repeatable.

It was also my first project using Gemini as an active development assistant, making the build both a product experiment and an early lesson in working critically with AI-generated guidance.

A lineup is more than a marker on a map

Counter-Strike utility is spatial, time-sensitive, and contextual. A useful reference needs to communicate where a player stands, where they aim, what they throw, and how that throw fits with the rest of an execute.

Multi-level maps make that harder: flattening every location into one image can make otherwise correct information ambiguous.

Keep the interface fast without losing tactical detail

Spatial clarity

Represent maps with multiple levels without making navigation or lineup discovery confusing.

Utility vocabulary

Distinguish grenade types, throwing positions, targets, and timing in a consistent visual system.

In-round usefulness

Keep the path to a lineup short enough that the site remains practical while learning or preparing an execute.

Available assets

Source map imagery suitable for building a clear and consistent reference experience.

A compact application with an automated path to production

The application paired a Next.js and React frontend with PocketBase for lineup data and Steam-linked user accounts. Docker made the runtime portable, while GitLab CI/CD, the container registry, and self-hosted runners connected a passing commit to deployment on a personal server.

The application container remained behind the public edge. Nginx handled incoming traffic and Certbot managed Let's Encrypt certificates independently of the application image.

  1. 01

    Next.js and React

    Render the map, lineup, timing, account, and execute-grouping interfaces.

  2. 02

    PocketBase and Steam

    Store application data and connect player accounts through Steam OpenID 2.0.

  3. 03

    GitLab delivery

    Validate the project, build the Docker image, and publish it to the container registry.

  4. 04

    Self-hosted production

    Runners deploy the image behind Nginx, with Certbot managing TLS certificates.

Model the information around how players use it

Separate map levels

Treat vertical map layers as a first-class navigation concern rather than forcing every location onto one surface.

Explicit utility types

Make grenade type, timing, origin, and target easy to scan so lineups can be compared quickly.

Group lineups into executes

Allow multiple utility throws to be organized together, including timing, so the tool supports coordinated play rather than isolated examples.

Choose a small backend

Use PocketBase to cover the project's data and authentication needs without introducing a larger service architecture.

Use Steam's browser identity flow

Authenticate players with Steam OpenID 2.0, matching the product's audience without introducing a separate account system.

Automate every accepted change

Let a passing GitLab pipeline produce and deploy the container rather than relying on manual server updates.

From individual throws to coordinated executes

Map-centered discovery

Organize lineups around the game map and its vertical levels so users begin with the same spatial context they have in play.

Repeatable throw records

Combine grenade type, throwing position, aim point, and timing into a consistent description of each lineup.

Execute composition

Group individual lineups into coordinated map executes so timing and utility can be reviewed as one plan.

Identity and delivery

Use Steam OpenID 2.0 for browser sign-in, then package and deploy the application through the shared GitLab and Docker workflow.

The application was only half the project

Learning to work with AI

Using Gemini effectively meant learning when it could accelerate exploration and when its suggestions needed verification or a different approach.

Representing vertical maps

Lineups on different floors needed enough separation to stay unambiguous without fragmenting navigation.

Communicating a complete throw

Utility type, timing, origin, and target all needed to remain readable without overwhelming a quick reference.

Building the deployment path

The personal server, GitLab pipeline, Docker image, and self-hosted runner all had to work together before a passing commit could become a running release.

Sourcing map imagery

Useful map views were foundational to the product, but finding and preparing the right source images was its own constraint.

Choosing a public identity

Shipping the first application also meant choosing a domain and establishing the deployment conventions later projects would inherit.

A usable product and a repeatable deployment workflow

A working lineup reference

The deployed site presents utility lineups in a form players can browse and use.

Execute planning

Users can group lineups and represent their timing to build coordinated executes for a map.

Infrastructure experience

The project established hands-on experience running a containerized application through GitLab CI/CD onto personal infrastructure.

AI-assisted development experience

It provided an early, practical understanding of both the leverage and limitations of an AI coding partner.

Shipping the system changed what the project taught

Nades made the boundary between building a feature and operating a product concrete. The UI, data model, container, pipeline, runner, and server were all part of whether the application actually worked for someone.

It also established a pattern I still value when using AI: use it to widen exploration and shorten feedback loops, but keep technical judgment and verification with the engineer.

Document, validate, and refine

A future pass can document the current implementation against the source, refresh any map data affected by game updates, and add screenshots showing the multi-level map and execute-building flows.