Case Studies
What each problem was, what Sachintha Gunaratne decided, and how it turned out.

MCQ Karamu
MCQ practice app on Google Play for Sri Lankan students sitting A/L and O/L exams. Built with Akila Kavinda.
Objective
Past paper practice here is a stack of PDFs, and students sit the exam in Sinhala, Tamil, or English depending on where they went to school. Language is the first thing an exam app drops, and most of these students have no card to pay with.
Key Decisions
Made the question model trilingual in the first version instead of shipping English and translating later.
Three variants of every question to author, in exchange for Sinhala and Tamil students getting the same app rather than a worse one.
Billed subscriptions to Dialog and Mobitel phone accounts rather than to cards.
A revenue share and two telco integrations, in exchange for students without a bank card being able to subscribe at all.
React Native on Firebase, with no server of our own to run.
Less control over the data layer, in exchange for two people shipping and maintaining the whole product.
Impact
- Live on Google Play with subject-wise past papers, timed exams, instant results, and progress tracking.
- Every question bank in Sinhala, Tamil, and English, so a student practises in the language they will sit the paper in.
- Students subscribe from a prepaid phone balance, on Android 7 and up.

Ideamart & MSpace Subscription Service
Open source Express service that puts Dialog Ideamart and Mobitel MSpace subscription billing behind one API. Pulled out of MCQ Karamu and released on its own.
Objective
Carrier billing is the only realistic way to charge a Sri Lankan student with no bank card, and each telco has its own OTP flow and documentation that is hard to follow. I wanted one set of endpoints the app could call without knowing the network, and a way to stop one subscription being passed around a class.
Key Decisions
One interface over both providers, with the differences absorbed in a mapping layer.
A layer to maintain as either platform changes, in exchange for an app that never branches on carrier.
Read subscription status from the carrier on request instead of mirroring it locally.
Entitlement checks need a live call to the telco, in exchange for access that matches what the carrier actually charged.
Bound each account to one active device, so registering a new phone displaces the old one.
A student changing phones has to re-register, in exchange for one paid subscription not covering an entire class.
Impact
- OTP, unsubscribe, status, and charging info for both carriers behind one set of endpoints.
- Runs the billing behind MCQ Karamu.
- Open source, as a working reference for an integration the official docs cover poorly.

XR Room
This site as a WebXR space. Every section stands as a panel in a ring around you, in a VR headset, through a phone camera in AR, or turned by a phone's gyroscope.
Objective
My research asks how much AR a mobile browser can do when the user installs nothing, and until now this site only said so. The room had to carry the whole site into AR with no app, cost the home page nothing, and keep its text readable through a phone camera.
Key Decisions
Gave the room its own address at /xr instead of a mode toggled on the home page.
A second page to keep in step with the first, in exchange for a home page that never loads the 3D code and a room you can link to.
Built every panel from the same data modules as the site, not from its rendered pages.
The panels show what the data says rather than how the site lays it out, in exchange for copy that can't drift between the two.
Stood the panels in a ring and left out walking entirely.
Nothing to explore, in exchange for a room you read by turning your head, with no teleporting to learn.
Rendered the text as signed distance fields instead of drawing pages to textures.
A font pipeline and a layout pass of my own, in exchange for text that stays sharp at reading distance.
Showed desktops without WebXR a QR code to the room instead of a 3D view to drag around.
Nothing to look at on a laptop, in exchange for sending those visitors to a device where the room is actually spatial, with the reason their browser can't open it.
Made three.js fall back to a plain WebGL layer when the browser won't enable WebXR layers.
A workaround around the library's own session setup, in exchange for AR that shows anything at all on Android Chrome, which otherwise opened onto an empty camera feed.
Impact
- The home page ships the same 650,026 bytes of JavaScript as before the room existed.
- The room is a single 173 KB gzipped chunk, fetched only on /xr and only after the page has rendered.
- Verified in AR and the gyroscope view on an Android phone with ARCore, and in the gyroscope view on an iPhone. VR has not been checked on a real headset.