The whole stack. One person.
Modern web apps with a CMS your team can actually use. Researched with the people who will use them, then designed, built, secured, hosted and run by me. Interface to DNS.
How I build
Slim servers. Small Node services with thin handlers, behind a reverse proxy, with one identity service for every app. Several production apps share one 2GB server. You get speed and a small hosting bill.
JSON throughout. Content is structured data from a headless CMS. The same data feeds your website, an app, a dashboard or another system without rework.
People first. I research with the people who will use it, then test with them. Accessible to WCAG 2.2 AA from the first commit.
Server-rendered. Pages arrive complete. JavaScript makes them better. It is never needed to read them.
Built in CI, not on the server. Few dependencies, so less to patch and less to break. Servers run code. They never build it.
Yours to keep. Standard and portable. It runs on a VPS, on Vercel or in a cloud, and you can take it anywhere.
The lot
Every layer, from the people using it down to the network. No subcontractors. No gaps.
-
Research and design
I find out what people need before I build it, then test it with them.
- User research
- Interviews
- Usability testing
- Prototyping
- Interaction design
- UX
- Accessibility to WCAG 2.2 AA
-
Architecture
Designed for the job and sized to run fast on modest hardware.
- Headless CMS
- JSON APIs
- Small services
- Server rendering
- Pre-rendering
- Progressive enhancement
-
Web apps with a CMS
Your staff edit the content. Nobody phones a developer.
- Payload
- Directus
- A custom CMS written for the job
-
Interface
Hand-written where that is enough, a framework where the app warrants it.
- HTML
- CSS
- JavaScript
- React
- Next.js
- TypeScript
- Tailwind
-
Application and data
APIs, databases, and the data you already have, moved in safely.
- Node
- Express
- Fastify
- REST and JSON APIs
- Postgres
- SQLite
- Prisma
- Full-text search
- Data migrations
- Legacy imports
- Feeds to and from other systems
-
Identity
One sign-in for every app, with roles that match how your organisation works.
- Passkeys (WebAuthn)
- OIDC
- Single sign-on
- Google sign-in
- Magic links
- Role hierarchies
- Central identity service
-
Payments and ticketing
- Stripe
- Box office
- Events
- Bookings
-
Integrations
- Social media hooks
- Webhooks
- Media pipelines
- Third-party feeds
-
AI
LLM features behind a provider you can swap, with privacy designed into the schema.
- LLM features
- Swappable provider interface
- Privacy by schema
-
DevSecOps
- GitHub Actions
- Automated deploys
- Dependency patching
- Content Security Policy
- Security headers
- Secrets management
- pm2
- Docker
- Monitoring
- Health checks
-
Hosting
Several production apps on one small box.
- Linux VPS
- nginx
- Vercel
- Google Cloud
- Managed Postgres
- Backblaze B2
- Object storage behind a CDN
-
Cloudflare
- DNS
- CDN
- Caching
- TLS
- Edge protection
-
DNS and email
Your domain cannot be spoofed, and your mail arrives.
- DNS management
- Domain migrations
- SPF
- DKIM
- DMARC to p=reject
- BIMI
- MTA-STS
- Mail forwarding
- Mailing lists
- Transactional email
-
Network
- Reverse proxies
- TLS
- Private networking
- VPN (Tailscale)
- Network design
-
Policy and governance
I write the policy as well as the code.
- Data protection by design
- Accessibility
- Security policy
- Safeguarding
- Regulatory compliance
Work
A charity platform, small-business sites on custom CMSs, university systems and a public archive.
-
Solent Gay Men's Chorus
Registered charity. The whole platform, built and run by me.
- One identity service for every app, with passkey sign-in and a role hierarchy.
- Stripe payments, box office and ticket sales, and events.
- Media library on object storage behind Cloudflare.
- Social media hooks, member email and a CMS the chorus edits itself.
- Five Node services: main website, box office, governance, members portal and identity. Content in Payload.
- All five share one 2GB server behind nginx, deployed from GitHub Actions.
- Email domain at DMARC p=reject, with DKIM aligned.
-
SUMS
Blind double-marking and moderation platform for a university.
- In production since 2018, on Google Cloud with single sign-on and a full audit trail.
- About 600 projects a year: 350 undergraduate and 250 master's, plus resits and two international partner programmes. About 100 markers a year.
- More than 4,800 projects marked since 2018.
-
Student feedback platform
Commissioned by a university, designed from research with students and staff.
- Next.js, TypeScript, Postgres and Prisma.
- Anonymity is enforced by the schema: a response cannot be joined to the student who wrote it.
- LLM features sit behind a swappable provider interface.
-
Survey analysis dashboard
Adopted across a university faculty.
- Whole-institution survey results analysed in three hours.
-
geraldlarner.com
A public, searchable archive, built from a pile of old files.
- About 6,700 legacy word-processor files, covering 444 composers.
- A thin Express server over one SQLite file of pre-rendered pages, with full-text search and a JSON API.
-
alicedennis.net
Site for a piano and singing teacher. Replaced a broken legacy site.
- A custom lightweight CMS with a built-in admin page and Google sign-in, so the owner edits it herself.
- Express on my VPS.
-
morganharnett.tattoo
Site for a tattoo artist, on a custom CMS written for the job.
- It fills itself from Instagram, so the artist updates one place.
-
Photo archive for a school
Private. Built on Directus.
- Artists' photographs managed in one place so students can learn from them.
-
ASICA
Research tablet app for melanoma self-monitoring.
- Designed with patients and run in a six-month NHS pilot.
Receipts
Figures about this page, measured by the server that sent it. None of them are typed in by hand.
- Page weight
- 9.8 KB Everything this page downloads, compressed, in 4 requests. Check it yourself: page weight
- Server render
- 0.14 ms Median of the last 21 renders. 95% took under 3.54 ms. Check it yourself: Server-Timing response header (browser dev tools, Network tab)
- Server memory
- 21.7 MB What this server holds on its own, right now. Node's shared program code is not counted. Check it yourself: server memory
- Third-party requests
- 0 The content security policy lets the browser load from this domain only. Check it yourself: third-party requests
- Runtime dependencies
- 2 69 packages installed in total, counted from the lockfile. Check it yourself: runtime dependencies
- Cookies set
- 0 Out of 3 responses since the server started. Check it yourself: Application tab in your browser's dev tools
- Version
- 6dc7065 Deployed 2 October 2026 at 14:59 UTC. Check it yourself: version
- DMARC policy
- p=none Read live from DNS for passionfruit.design. Check it yourself: dmarc policy
- Security headers
- This response was served with:
Content-Security-PolicyStrict-Transport-SecurityX-Content-Type-OptionsX-Frame-OptionsReferrer-PolicyPermissions-PolicyCross-Origin-Opener-PolicyCross-Origin-Resource-PolicyCross-Origin-Embedder-PolicyOrigin-Agent-Cluster
About
I'm Matt. I'm a human-centred designer who writes the code and runs the servers.
My doctorate is in human-computer interaction: how software should adapt to the person using it. User research, UX and accessibility are what I trained in, not something I added later.
I'm an academic. I have taught web technologies for over a decade and published peer-reviewed research. I have run production systems since 2018.
I'm a member of the BCS and a charity trustee, and I have held a senior role covering safeguarding, regulatory compliance and data protection. I write the policy as well as the code.
Tell me what you need
A few lines about the project is enough. I read every message myself. Or email hello@passionfruit.design.