Software Engineer • IT Support Technician • Computer Science Student
I'm a Computer Science student at the University of KwaZulu-Natal and an aspiring software engineer with hands-on project experience in C++, Java, and Python.
From building a multi-threaded C++ inventory system for a simulated logistics network, to fine-tuning a BERT model on 60,000 Steam reviews, to writing a music player that analyses audio itself rather than trusting metadata. I like projects that force me to actually understand how something works under the hood.
Alongside my studies, I've spent over five years in IT support and client-facing sales at RM Graphic Solutions, diagnosing hardware faults, building and configuring custom PCs, and managing client accounts from enquiry through to delivery. That combination means I approach software with both a builder's curiosity and a support technician's instinct for what actually breaks in the real world.
Come take a look around. I'm always keen to talk about a new project or opportunity.
A high-performance C++ system built for a fictional client, Nexus Logistics, to centralise inventory and order processing across 5 warehouses that had previously each run their own inconsistent management system. The design follows a producer-consumer model: five warehouse threads load orders into a shared queue, then a user-selected number of worker threads (1-32) drain and fulfil them concurrently against a central inventory.
I led the project and was responsible for the high-level system design: the overall producer-consumer architecture and how the OrderPool, InventorySystem and thread pipeline fit together. I then built the entire CLI: the full 9-option menu, every product/search/sort sub-menu, and the input-validation layer (safe int/double readers that recover cleanly from bad input instead of leaving cin in a broken state). Together these cover all the user-facing flow that sits on top of the class hierarchy the rest of the team designed.
I also took the AnalyticsManager through a full overhaul, from a handful of basic totals into a proper reporting engine: 18 metrics across global and per-warehouse scope (revenue, lost revenue, best-selling item, and category/type breakdowns of revenue, quantity, failure rate and average price), each exportable to its own CSV for Excel/Power BI, all runnable from a dedicated Analytics sub-menu I added to the CLI.
Product base class with Taxable, Discounted and Standard subclasses, using virtual functions so pricing logic resolves correctly through a shared_ptr<Product> at runtime.std::atomic<int> for lock-free order-ID generation.Processing 1,300 orders across a 999-product, 11-category catalogue scaled from ~20,000 ms on a single thread down to ~650 ms at 32 threads (a ~30× speedup) before plateauing as inventory-mutex contention became the bottleneck.
A full-stack database application modelling the day-to-day operations of a travelling amusement park (customer admissions and loyalty cards, employee rostering, ride/show/stall management, maintenance logging and financial transactions), all served through a role-based Java Swing front end over MySQL.
I planned the business scenario that the whole database was modelled from: working out how a travelling carnival's customer admissions, loyalty smart cards, employee roles, attractions and financial transactions actually relate to one another, before any table existed. That scenario became the spec the EERD and schema were built against.
I then built the entire Java Swing front end and its visual design. Login is a custom-painted screen I wrote directly on Graphics2D: a drifting starfield of 180 twinkling stars, 18 looping carnival “sparks”, and a slowly shifting dual-tone gradient behind a rounded glass card, all hand-animated on a Swing Timer, no game engine or extra library involved. On top of that I built the generic EntityConfig-driven CRUD engine that every other screen is built from, the role-based routing between the four account types, the four-step Sales pipeline, and the Reports screen.
EntityCrudPanel builds the form, grid and SQL for all 21 entities: adding a new entity is a metadata entry, not a new screen.All queries are parameterised throughout, and MySQL's own per-role privileges provide a second line of defence: even a bug in the Swing client can't issue a statement the account isn't allowed to run.
A head-to-head comparison of three generations of NLP text classifiers (a traditional statistical model, a recurrent deep-learning model, and a pre-trained transformer) on the same binary sentiment task: predicting whether a Steam game review is Recommended or Not Recommended.
I led the project and, in addition to training our BERT model, built the entire data pipeline that fed all three models (extraction, cleaning, balancing and splitting) as a standalone notebook the rest of the team's model notebooks all pointed at, so every model trained and tested on identical data.
Extraction: Steam's API caps each request at 100 reviews and paginates with a cursor rather than an offset, so I wrote a loop that walks that cursor per game across 100 selected App IDs, targeting 500 positive and 500 negative reviews each. I collected by class as I went, not after the fact, because some games skew heavily positive and pulling reviews blind would have starved the negative class. Requests are rate-limited with time.sleep() to avoid throttling, and every write checks Steam's own review ID before saving, so re-running the script after a timeout or crash never reintroduces a duplicate.
Cleaning: the raw pull (~94,900 reviews) needed several passes before it was trustworthy training data: stripping reviews mangled by Steam's profanity filter (which replaces swearing with a symbol that corrupts the surrounding text on read), removing links and spam patterns that carry no sentiment signal, running langdetect per review because Steam's own language tag reflects the client's UI language rather than the review text (this alone cut 21,950 reviews), stripping emojis the tokenisers can't meaningfully use, and de-duplicating by exact text (~18,000 reviews) so the same review couldn't land in both the train and test split and inflate accuracy.
Balancing & splitting: the cleaned data was balanced to an even 60,000 reviews (30k positive / 30k negative) and split 70:30 with stratification, so class balance held in both the training and test sets.
For my own model, I fine-tuned BERT (bert-base-uncased) with AdamW, a linear warmup schedule and gradient clipping on a 6GB GTX 1060, and restored the best-validation-loss checkpoint to counter overfitting after epoch 1.
A local player for FLAC libraries, inspired by the old Samsung TouchWiz Music Square, except instead of relying on genre tags or an online lookup, it plots every track by actually analysing the audio itself. Everything runs locally: no files, tags or audio ever leave the machine.
For each track, librosa extracts tempo, loudness (RMS), onset rate, spectral centroid, and a tonality “mode score” from a chroma vector compared against major/minor key profiles. These five raw features are then z-scored across the whole library (so “loud” or “bright” is always relative to your own collection) and combined into two weighted axes: arousal (tempo-led) for the x-position and valence (tonality-led) for the y-position, each compressed to -1..+1 with a tanh squash.
The analyzer is resumable and parallelised: it samples ~60 seconds per track from ~30% in, skips already-scored files on re-runs, and falls back to a slower crash-isolated mode if a corrupt file would otherwise take down the whole batch.
The site you're looking at right now. A single-page Jekyll portfolio with sliding panels for About Me, Projects and a full Detailed CV, all themed and colour-matched to my actual CV rather than a generic template look.
Rather than starting from a blank page, I built this on top of two open-source Jekyll templates and reworked both substantially: