jaiden.dev

Project Spotlight: ge-skiller

A useful tool for poor Old School RuneScape characters

SvelteKit
MongoDB
Tailwind
Personal
ge-skiller image

Built With

The tools and libraries behind GE Skiller.

SvelteKit

Full-stack Svelte 5 meta-framework handling file-based routing, server endpoints, and SSR across the app.

TypeScript

End-to-end static typing spanning the UI, data-ingestion scripts, and the OSRS Wiki scraping spider.

MongoDB

Document store holding ~13,000 in-game items with recursive, self-referencing ingredient trees.

Tailwind CSS

Utility-first styling for the clean, responsive interface across every route.

shadcn-svelte

shadcn/ui ported to Svelte, built on bits-ui primitives — accessible components that live in the codebase.

Apache ECharts

Renders the animated, interactive ingredient trees that morph as you navigate between items.

Superforms + Zod

sveltekit-superforms paired with Zod schemas for type-safe, validated add/edit item forms.

Overview

My earliest memory of Old School RuneScape is a conversation two of my friends were having in what I think was the fourth grade. Eric said to another friend (I don't remember who; sorry, other friend) that he had some armor and weapons for sale in the game. I asked what they were talking about and learned about RuneScape for the first time. That evening, I tried playing the game on our home PC, but it wouldn't load; just a black page. At that age, computers were still a big mystery to me. I didn't know I probably needed to install Java, or what that even meant, much less how to do it.

By the next year, I'd switched schools and soon enough RuneScape came up again. This time it was my friend Michael telling me about it. He helped me get it installed (probably walked me through the Java installation, too) and I was immediately hooked. I remember getting so pumped when I'd get home from school and hear the login music... Man, I could get chills just remembering those days. Days of friends and simplicity. I'm very grateful to have experienced those years the way I did because I know not everyone was so fortunate.

Jaiden excited to play on the computer -- any ideas what game he's about to play?

Fast-forward 20 years and I'm still playing RuneScape. Like probably millions of others, when it was announced they were bringing our version of the game back as a standalone title, I was ecstatic.

In late 2023, I found myself wishing I had an idea for a web project to spend some time on so I'd been on the lookout for ideas. One day, as I was thinking about this game and my ongoing issue of being perpetually poor within it, I found myself wishing for a tool that would analyze my account and tell me the most profitable items I could produce given my skill levels, quest completion status, and so on. I realized this idea was as good as any, and got to work.

Early days

Though it's undergone many iterations, GE Skiller has always been a SvelteKit app. I started before the Runes feature was introduced and tried several UI options before finally landing on shadcn-svelte and developing the app into what it has become today.

In the spirit of Svelte's "it works with anything JS" approach, I wanted to find a component library that was minimal and as simple as possible. That first landed me on Pico CSS. Less of a component library and more of a stylized CSS reset, Pico gave me a decent jumping-off point to help flesh out the most basic ideas of the app.

I knew I wanted to:

  • see items from in-game
  • filter those items based on which ones I could make (with smithing, crafting, fletching, etc)
  • see a breakdown of what materials went into creating a given item

With those ideas in mind, and Svelte and Pico in my toolbelt, I set out to create my first prototype. Pico was a pleasure to use, but I wanted something more fully featured. I was in a place where I was alright with leaning on a component library, and while I saw the value in Pico, I still wanted more.

Despite a massive data loss event in 2024, a couple of videos of the app from that time survived 🥹. Here's one showing what it looked like early on with the above goals in mind.

Soon after, I discovered skeleton.dev. Skeleton was a Svelte-only component library, and the best one at that. It offered more high-quality Svelte components than any other Svelte-based option at the time. It was an obvious choice.

Soon after making the pivot to Skeleton, however, its developers embarked on a journey to transform Skeleton into much more than just a component library. I was excited to watch its transformation into the powerhouse design system it has become today, but I decided to sit out the migration to later versions of Skeleton, so to an extent I started over on GE Skiller. (I'd still love to use Skeleton again on a future project 😉)

By this time, the Svelte component library ecosystem had matured, and I found that a favorite of mine, shadcn-ui, had been ported to Svelte. After checking out the port, I knew I had to try it. That was when GE Skiller in its current form began. Up until this point, it had been called "OSRS GE Skiller" (and you can actually still access the old repo with Skeleton here), so I renamed it to the much catchier "GE Skiller."

(Sarcasm, obviously. If you can think of a better name PLEASE let me know.)

The Emperor Website's New Clothes

GE Skiller website before restyling
GE Skiller website after restyling

With shadcn-svelte in my corner, it was time to go to work recreating the app. I started with the homepage and basic app layout, and those haven't changed much since its creation. As a "just for fun" thing, each time the page is loaded or refreshed, you'll get one of 15 or so iconic NPCs and outfits from the game.

GE Skiller homepage

Next, I designed the database. Any in-game item could theoretically have ingredients that make it up, so it was important to allow for recursion in ingredients. Despite using a JSON-based DB (as opposed to SQL, for example), I went with what was essentially still a "foreign key" approach where every item would exist independently and simply reference other items if they had ingredients. The full contents of that nested data can be hydrated before showing it to the user.

With that in mind, I took my 20-something hard-coded items and inserted them into the database. Paired with an assist from the OSRS Prices wiki, each card now showed a price and some other helpful info. Now we're cooking!

GE Skiller cards UI

By now you can also see the app has taken on a new aesthetic. While it's similar to many apps out there today, I enjoy this look and feel satisfied making a website that looks so clean. I'm sure this look will eventually grow as tired as the original Material Design, but today we enjoy it!

GE Skiller cards UI

I used an older, well-established library called echarts to render the item ingredients tree. To my surprise, this lent itself very well to an SPA-style application where, instead of linking between actual pages, the contents of the same page are simply replaced. Because of this behavior, the chart will animate between pages. Try opening the page in the screenshot above and then clicking one of the ingredients within the tree to see what I mean!

Welcome to Mongoland - Population 10,000

MongoDB was meant for humungous amounts of data. But I love using it for its simplicity. I populated my MongoDB collection with items using a tool named OSRSBox, and later, a more up-to-date derivative named osrsboxed-db. The resulting 13,000 items might make MongoDB overkill, but I find the ease of use to be well worth reaching for something so powerful.

Populating the database was one of the most fun and novel parts of the entire experience. No one has time to crawl thousands of individual items, pulling their data piece-by-piece to populate a DB, so this was when I decided to get some more experience using LLMs. My initial idea was to see if I could have an agentic LLM crawl the Wiki and extract all the data I needed, but this was proving unreliable and difficult to set up. I pivoted to a more traditional approach:

  1. Clone the osrsboxed-db repo for local access to item data (tens of thousands of items!)
  2. Write a script that extracts the items into the MongoDB collection
  3. Write a script that reads the Wiki for each item and pulls any creation/ingredient data. This step will highlight what items are missing after extracting them all from osrsboxed-db.
  4. Write a spider to scrape the missing data

I find LLMs to be hugely beneficial when you have a framework in mind for how to move forward. If you can articulate the approach clearly, LLMs can often close the gap, or at least get you close. They're wonderfully, scarily capable. I used ChatGPT to scaffold most of this code and saved myself quite a lot of time as a result.

I heard an OpenAI engineer express that, when he's writing his own code, there are two primary bottlenecks: the mind and the hands. Either you're slowed down by needing to plan and learn, or you know how to move forward, but you can only code so fast. This resonated quite a bit with me because, barring technical hiccups and disruptions, I find it to be true.

I know how to fire off a request to some API endpoint and receive data. I know how to dissect the data, ingest it into my app to get what I need out of it. I know how to write a spider that crawls websites to extract what I need from them. I know how to insert that data into my database. So what do I lose by asking ChatGPT to do it?

I'd lose my old way of working, as it turns out.

How to Conduct a Symphony

How many hours have you spent learning the things you're good at? By this point, I've spent thousands of hours learning web development. I was fortunate enough to get into this field before LLMs were around to eradicate the junior dev position. But even with the knowledge I have, I can only achieve so much alone.

If a person wants to make epic, moving pieces of music, how can they do so when they can only play one instrument at a time? I suppose they could instead conduct a symphony.

As a developer, having an LLM to work alongside me massively speeds up my ability to deliver. I can ask the model to adjust the padding of this element, the color of that element, the text content on this page and that one. It can execute faster than I can and more quickly fill any gaps that may be present in my knowledge. In this way, I go from playing one instrument to conducting the whole symphony. The resulting music is amazing despite not playing every instrument myself.

I worked with ChatGPT through the Codex feature (massively underrated, by the way!) to iteratively write and course-correct the trickiest parts of the process. I set up environment variables and MongoDB collections, cloned the repo of items, then had GPT write the code that would step through each item and upload the relevant data to the database. Before an item is written to the database, we parse the Wiki page for the item, looking for ingredients within a "Creation" section.

Many items were missing at this point, but admittedly, I don't know if it's because of actual missing data in osrsboxed-db or because I accidentally filtered out that many item ingredients when dumping the items into my database. In either case, the next step was going to involve crawling the wiki for more than just creation specs, so I was excited to get started.

While the spider is a standalone script, I ended up incorporating its functionality into an API endpoint. This endpoint, active only in development, allows me to search for item data. It'll grab any info it can from the OSRS Wiki API, and for anything else, it'll parse the wiki page for that item. I attached this functionality to an "Add/Edit Items" dialog (also only accessible in development) so I now have an easy-to-use UI for adding and editing item data. This dialog was actually one of the more impressive moments I had working with Codex. I asked for this dialog to exist as a feature, and Codex took about 10 minutes to plan and execute. There were a couple small adjustments needed, but for all intents and purposes, it basically nailed this fairly complex feature on the first shot. It kind of blew me away.

This project and article are a work in progress.