Before you come in

How do you want to read this?

The same words, three different rooms. You can change your mind whenever you like.

joseph-afif

developer & coordinator

uppsala, se

open to freelance

// developer and coordinator, uppsala

JosephAfif

I run a summer-work programme, and I build the software it runs on. The rest of the time I build things that were harder than they needed to be.

studying: computer science, uu ferieappen: 2 summers live available: freelance

// who

I wrote code for years before any of it was for anyone else. Then the summer-work programme I coordinate needed a system, and that became the first thing other people depended on.

Seventy young people applied on paper the first year. Someone had to read each handwritten form and copy it into a spreadsheet by hand. That someone was me, and it took four working days before the season had even started.

I build for people I can talk to. Everything on this page came out of sitting with whoever had to use it, not out of a specification. Most of my attention goes to the parts nobody demos: what happens to the personal data, and what happens the day someone types the wrong thing.

Away from all this I play football and make music. I start a Computer Science degree at Uppsala University this autumn, and I take freelance work alongside it.

role
developer, and coordinator of the programme I build for
based
Uppsala, Sweden
studying
Computer Science, Uppsala University, 2026–2029
strongest
PHP, MySQL, WordPress, JavaScript, TypeScript, Dart, Python
also
Flutter, Kotlin, React, Tailwind, WebGL, Supabase, FastAPI, SQLite
done
PDF generation, e-signing, encryption, GDPR, OpenStreetMap, LLM tool calling
available
freelance and client work, alongside studies
speaks
Swedish, English
licence
B

// in production, other people depend on these

work

The Ferieappen front page: a job search with live listings from Uppsala kommun, Platssamverkan Gottsunda Valsätra and a demo municipality.
assets/shot-ferieappen.webp

ferieappen

Summer-work programmes end to end. 130 workers, 12 000 hours.

  • Flutter
  • Supabase

2025–

ferieappen

The whole chain, not one piece of it: application, admission, contract, schedule, attendance, work certificate.

what it replaced

  • handwritten forms and Excel lists → application and admission in one place
  • print, sign, scan, binders → contracts signed on a phone
  • attendance counted by hand → time reports that become payroll data
  • private numbers and Snapchat groups → messaging inside the system

the constraint

Most users are under 18. Personal data is encrypted and the whole thing is built to GDPR. Every feature exists because something broke during a real summer.

stack

backend
Supabase
app
Flutter
sign-in
the web app, or the Flutter build
documents
PDF generation, electronic signing
shipping
an APK today, App Store and Play Store next
The Platssamverkan site: an aerial photograph of Gottsunda behind the headline, with cards for the job portal, Ferieappen and the summer workers.
assets/shot-platssamverkan.webp

platssamverkan

Site, intranet, time reporting and contract generation. Unpaid.

  • WordPress
  • PHP
  • MySQL

2026–

platssamverkan gottsunda / valsätra

The public site, plus the systems the association runs its staff and summer workers on.

what I built

  • the WordPress site
  • an intranet for tasks, training and scheduling
  • time reporting with an absence statistics view
  • a generator that produces the employment contracts

why it is unpaid

The association is run by property owners and the municipality, and the budget goes to the programme. It is also where Ferieappen was built, which is why the product looks the way it does.

stack

site
WordPress, custom plugins
intranet
EDIT: inside WordPress, or separate?
reporting
PHP, MySQL, statistics module
The Renyx site: a living room behind the headline, with booking and contact buttons and the current offer.
assets/shot-renyx.webp

renyx

Site, booking flow and a price calculator. Built with the founders.

  • WordPress
  • PHP

2026

renyx

Site, booking flow and a price calculator for a cleaning company in Stockholm.

the hard part was not the code

  • what counts as a room
  • how a move-out clean differs from a deep clean
  • where the price stops being automatic and becomes a conversation
  • the rates are theirs, set in wp-admin, so a price change never waits on me

Weeks of back and forth with the founders, directly. Most of it about the calculator.

stack

calculator
square metres, priced against their own rates
rates
theirs to change, in wp-admin
booking
EDIT: where does a booking land?
built
WordPress, PHP

// built to find out whether I could

projects

The simulator running Uppsala at morning peak: the road network coloured by congestion, the control panel on the left, and live figures along the bottom.
assets/shot-trafiksimulator.webp

trafiksimulator

A whole Swedish town, simulated. 22 800 links, 55× realtime.

  • TypeScript
  • WebGL2
  • OSM

2026

trafiksimulator

OpenStreetMap in, a traffic model out: lanes, priorities, roundabouts, signals. Demand comes out of the buildings rather than being typed in.

how a whole town fits at once

  • near you, every car is simulated individually
  • everywhere else, queue and server per link
  • same vehicles either way, switching mode mid-journey without losing their route

two things I could check

  • residents derived from buildings 185 000, actual 170 000, on a number never fed in
  • saturation flow measured 1 700 vehicles per lane per green hour, which is what a real junction does

So: trust where the traffic stops and how much a change helps. Absolute flows on one street should be calibrated first, and the tool says so itself.

stack

language
TypeScript, no framework
simulation
Web Worker, typed arrays
rendering
WebGL2, instanced vehicles
models
Intelligent Driver Model, MOBIL, Webster

storstad

Mini Metro’s feel, plus a region you expand. No game engine.

  • Flutter
  • Dart

2026

storstad

Mini Metro’s feel, plus a region you expand. No game engine: everything is drawn with a CustomPainter.

routing

Dijkstra runs backwards from every station of a given shape, so each destination has a full table: from here, that line, that direction. Passengers change like real passengers.

chosen on purpose

  • arrivals are uniform, not Poisson, because bursts sink a network through no fault of yours
  • unique shapes weigh 5 each, so one lone star far out becomes a real bottleneck
  • 45 tests, including gesture tests and golden images

stack

built with
Flutter, Dart, no game library
geometry
octilinear track, collective parallel offsets
debugging
command file over adb, state dumped as JSON
platform
Android

netwatch

What is on your Wi‑Fi, and what honestly cannot be seen.

  • Python
  • FastAPI
  • SQLite

2026

netwatch

Scans the network, lists what is on it, and can watch one device for a day. SQLite on your own machine. Nothing leaves it.

what it refuses to do

ARP spoofing would give the same visibility with no setup at all. It is also an attack on your own network, and it is the exact technique this tool exists to detect. So it is not in there.

identity when everything moves

  • IP changes are just another row, because MAC is the key
  • when the MAC rotates too, a scored fingerprint from hostname, mDNS and DHCP options
  • every merge logged with its score, and reversible
  • 41 tests, none of which touch the network

stack

backend
Python, FastAPI, SQLite in WAL mode
capture
passive sniffing, DNS forwarder, ARP sweeps
frontend
HTML, CSS, JavaScript, no dependencies
live
Server-Sent Events

sleepguard

Wakes you in light sleep. Silent nudges, five layers of failsafe.

  • Flutter
  • Kotlin

2026

sleepguard

Catches you in light sleep. You give it the latest you may wake; it only ever moves earlier.

how it reads you

  • accelerometer at 20 Hz, summed into 30-second epochs
  • microphone for the sound of sheets, which the accelerometer misses
  • a vibration nudge, then two minutes of listening: move and it wakes you, lie still and it backs off
  • silent on purpose, because a pulse into the mattress reaches light sleep and morning sound is stress

the layer that matters

If the foreground service is refused permission to start, the app posts a notification whose channel carries the alarm sound. Android plays it without the process being alive at all.

stack

logic
Dart, unit tested, no Flutter dependency
native
Kotlin: sensors, exact alarms, surviving the night
privacy
audio becomes a decibel figure and is thrown away
platform
Android only

vilja

Builds your goals, watches the web. Runs entirely on the phone.

  • Flutter
  • Riverpod
  • Hive

2026

vilja

Tell it a goal. It creates the tasks, reminders and watches, then checks the web for what you are waiting on.

swappable by design

The agent, the chat and the watches talk to one interface. Gemini by default, any OpenAI-compatible backend behind the same interface, switched in the app’s settings without touching code.

nothing leaves the phone

  • Hive boxes on device, no cloud sync
  • API keys in the Android Keystore
  • watches checked when you open the app, so there is no backend at all

stack

app
Flutter, Dart, Riverpod
storage
Hive, encrypted storage for keys
ai
provider interface, Gemini or OpenAI-compatible
platform
Android

Smaller things live on github.

// get in touch

Freelance work, a system that needs building, or a job you think I should hear about. Email me.

josephafifuppdrag@gmail.com

linkedin github ferieappen