Full-stack developerNantesOpen to work

Kept since Last entry 5 apps released · 1 in progress · 11 packages

Adrien Lacourpaille

What I code in the evening, once the workday is done, one dated row each: the apps first, then the packages they share. I am looking for my next job.

Register of releases, apps then packages

  1. Taverla

    App · party games · live

    A shelf of party games sharing one room, one QR code and one set of screens. One screen runs the game, everyone else plays on whatever they have to hand, and the first to know it takes the round.

    all 11 house packages

    Analytics

    Full entryPlay live (opens in a new tab)
    Drawing: a host screen showing the room’s QR code and seated players, and a phone showing the buzzerBUZZERScan to join the roomSEATEDAdaFIRSTGraceLinusMargaretWaiting for the next roundROOM · BUZZERAdaBUZZFirst to know takes the round
    Drawn for this register, not a screenshot
    Shipped29 · IX · 2026

    What shipped

    1. Three games on one shelf: blind test, buzzer and quiz.
    2. One room, one QR code: everyone joins from the phone they already hold.
    3. The server stamps every buzz, so the fastest hand wins, not the fastest clock.
    4. Tested end to end: real sockets against a real server, Playwright journeys across two screens.
    5. English and French, light and dark, on a phone or a laptop.
    Source (opens in a new tab)

    Mechanism · one server, a socket to every screen

    Diagram: one server holds a WebSocket to the host screen and to every phone, and stamps each buzz as it arrivesOne serverHono · stamps every buzzHost screenruns the game1st · takes it2nd3rd—WebSocket to each phoneOrder is decided on arrivalat the server, not by thephones’ own clocks.
  2. on-record

    App · public record · live

    How French deputies actually vote, vote by vote. Every public vote of the Assemblée nationale, explained in plain French for readers who never followed a session, each figure traced back to the official record.

    all 11 house packages

    Analytics

    Full entryOpen live (opens in a new tab)
    Drawing: one figure about a deputy, wired to the single ballot it counts, on a stack of official vote sheets where each row is a vote and each square a ballotASSEMBLÉE NATIONALEPUBLIC VOTESONE FIGURE · ONE DEPUTYFORwith the group of the dayOFFICIAL RECORDevery page cites its sourceEVERY FIGURElinks to the votes it countsNO RANKING, NO SCORE
    Drawn for this register, not a screenshot
    Live1 · X · 2026

    What shipped

    1. Every public vote of every deputy, with the group they sat in on the day of the vote. No ranking and no score: every figure links to the votes it counts, and every page cites its official source.
    2. No server and no database: a nightly GitHub Actions job asks the Assemblée’s open data whether anything changed, rebuilds only what did and deploys. A night without a new vote downloads nothing and deploys nothing.
    3. The host publishes 20,000 files per deployment, and there are over 8,000 votes: they ship in blocks, one file per deputy, small indexes for lists and search, and the build checks its whole output against a budget.
    4. About 750 pages prerendered by the very loaders the browser runs, a fake fetch answering their data during the build, each with its own head tags and a place in the sitemap.
    5. Find your deputy from your commune or your address, in French, light and dark.
    Source (opens in a new tab)

    Mechanism · no server, a nightly job asks what changed

    Diagram: every night a GitHub Actions job asks the Assemblée’s open data whether anything changed; a new vote rebuilds only what changed and deploys to Cloudflare Pages, a quiet night downloads and deploys nothingNightlyGitHub ActionsOpen dataanything changed?new voteRebuildonly what changedDeployCloudflare Pagesno new voteQuiet nightno download, no deploy20,000 files per deployment,8,000+ votes shipped in blocks,the output checked against a budget.
  3. Séance

    App · training · installable, offline

    A bodyweight training programme drawn as a gymnastics manual, one plate per movement. It guides a thirty-minute session, keeps a journal and draws the progress curves — installable, offline, with reminders, and with every number kept on the device.

    Full entryOpen live (opens in a new tab)
    Drawing: a phone showing one movement plate of a session, marked offline and saved on this devicePLATE · MOVEMENTPush-upSetRestNEXT MOVEMENTOFFLINEevery route answersON THIS DEVICEevery number, nowhere elseJOINTS, NOT PATHS37 figures, chainedfrom the hip
    Drawn for this register, not a screenshot
    Shipped29 · IX · 2026

    What shipped

    1. Installable and fully offline: a hand-written Workbox service worker precaches the whole shell, and every route is served with the network cut, mid-session included.
    2. Reminders without a server: an in-app clock, Notification Triggers where they exist, and Periodic Background Sync reading the schedule from IndexedDB in the worker — and the settings screen says plainly what this browser can do.
    3. Progress charts drawn by hand in SVG and HTML: regularity counted in weeks, volume per week, waist and weight never on a shared axis, each walkable with an invisible native range input and backed by a table.
    4. Every number stays on the device: a JSON backup carries it elsewhere, and a seeded specimen profile shows the charts full without ever touching the reader’s own data.
    5. Thirty-seven animated figures drawn from joints, not paths: interpolation is polar and chained from the hip, so no bone stretches mid-movement.
    Source (opens in a new tab)

    Mechanism · zero server, everything cached on the device

    Diagram: inside the device, the app shell is precached by a service worker and every number lives in IndexedDB; the network is cut and there is no serverThis deviceApp shellprecachedService workerWorkbox · remindersIndexedDBevery numberNo servernetwork cutJSON backupthe only way out
  4. Analytics

    App · analytics · public dashboard

    Hono on Cloudflare Workers, D1 and Core Web Vitals, with a public dashboard. A row holds a path, a referrer host, a country, a locale, a theme and a device class, and there is no consent banner because there is nothing to consent to.

    This siteTaverlaon-record

    Full entryOpen live (opens in a new tab)
    Drawing: the public dashboard with page views, Core Web Vitals and the few columns a row holds, beside a slip listing what is never storedPAGE VIEWSPublic dashboardCORE WEB VITALSLCPINPCLSPATHREFERRERCOUNTRYTHEMEDEVICE/en/projectsgithub.comFRdarkmobile/fr—BElightdesktopNEVER STOREDcookieIPuser agentfingerprinthashTRACKER.JSunder 1 kB gzippedFED BY THIS SITE, TAVERLA AND ON-RECORDno consent banner: nothing to consent to
    Drawn for this register, not a screenshot
    Live30 · IX · 2026

    What shipped

    1. No cookie, no IP, no user agent, no fingerprint and no hash stored: a row holds a path, a referrer host, a country, a locale, a theme and a device class.
    2. No consent banner, because there is nothing to consent to.
    3. A page view reached from another page of the same site continues a visit; anything else starts one.
    4. tracker.js weighs under 1 kB gzipped, follows single-page navigation, and never runs on localhost or in an automated browser.
    5. Adding ?analytics=off to a URL stops counting that browser.
    Source (opens in a new tab)

    Mechanism · three apps report, one Worker writes a row

    Diagram: this site, Taverla and on-record each run tracker.js, which sends page views to one Hono Worker on Cloudflare; it writes a row of six fields to D1 and never stores a cookie, an IP, a user agent, a fingerprint or a hash; the public dashboard reads D1This sitetracker.jsTaverlatracker.json-recordtracker.jsOne WorkerHono · CloudflareD1 · one rowpathreferrer hostcountrylocalethemedevice classDashboardpublic · ReactNever storedcookie · IP · user agentfingerprint · hash
  5. Scoreboard

    App · live scoring · club hall

    A live scoreboard for sports clubs running several tables at once. Four screens in the browser — the big screen, the umpire console, the organiser console and the spectator view — share one Durable Object per event on Cloudflare’s free tier, with nothing to install.

    Full entryOpen live (opens in a new tab)
    Live3 · X · 2026

    What shipped

    1. Each umpire scores their own match from whatever device is at hand, and every match in progress shows up on one big screen across the hall.
    2. One Durable Object per event keeps the ordered scoring events in its own SQLite storage and fans them out over WebSocket.
    3. Points scored while offline are queued and resent on reconnect, and a mistake is undone in one tap.
    4. Scores, the server and timings are all derived from the events, so the organiser can fix a wrong score or move a match to another table live.
    5. Table tennis is the first ruleset; the scoring rules are pluggable so other sports can follow.
    Source (opens in a new tab)
  6. adrienlacourpaille.dev

    This site · in progress

    The register you are reading. React 19, prerendered per locale, English and French, light and dark with no flash, accessible primitives from react-aria, and a Lighthouse score gated in CI.

    all 11 house packages

    Analytics

    Lighthouse
    Gated in CI
    Locales
    en · fr
    Hosting
    Cloudflare Pages
    In progress
    1. @adrienlcp/react-router (opens in a new tab)

      The router provider every app takes.

      0.1.1Used by TaverlaUsed by on-recordUsed by SéanceNot used by AnalyticsNot used by ScoreboardUsed by This siteShipped
    2. @adrienlcp/i18n (opens in a new tab)

      Translates, and knows at compile time what each message asks for.

      0.3.0Used by TaverlaUsed by on-recordUsed by SéanceUsed by AnalyticsNot used by ScoreboardUsed by This siteShipped
    3. @adrienlcp/theme-preference (opens in a new tab)

      Keeps the chosen theme from flashing, down to the phone’s toolbar.

      0.3.0Used by TaverlaUsed by on-recordUsed by SéanceNot used by AnalyticsNot used by ScoreboardUsed by This siteShipped
    4. @adrienlcp/browser (opens in a new tab)

      Copies to the clipboard even over plain HTTP, where the Clipboard API does not exist.

      0.1.1Used by TaverlaUsed by on-recordUsed by SéanceNot used by AnalyticsNot used by ScoreboardUsed by This siteShipped
    5. @adrienlcp/react (opens in a new tab)

      The context helpers every React app starts from.

      0.3.0Used by TaverlaUsed by on-recordUsed by SéanceUsed by AnalyticsUsed by ScoreboardUsed by This siteShipped
    6. @adrienlcp/react-aria (opens in a new tab)

      The focus ring and class composition over react-aria-components.

      0.1.0Used by TaverlaUsed by on-recordNot used by SéanceUsed by AnalyticsUsed by ScoreboardUsed by This siteShipped
    7. @adrienlcp/styles (opens in a new tab)

      The reset and shared styles every project starts from.

      0.5.0Used by TaverlaUsed by on-recordUsed by SéanceUsed by AnalyticsUsed by ScoreboardUsed by This siteShipped
    8. @adrienlcp/result (opens in a new tab)

      Says whether something worked, without throwing and without null.

      0.1.0Used by TaverlaUsed by on-recordUsed by SéanceUsed by AnalyticsUsed by ScoreboardUsed by This siteShipped
    9. @adrienlcp/safe-storage (opens in a new tab)

      Takes the throw out of localStorage and returns a Result instead.

      0.1.0Used by TaverlaUsed by on-recordUsed by SéanceUsed by AnalyticsUsed by ScoreboardUsed by This siteShipped
    10. @adrienlcp/tsconfig (opens in a new tab)

      The compiler settings every project extends.

      0.1.0Used by TaverlaUsed by on-recordUsed by SéanceUsed by AnalyticsUsed by ScoreboardUsed by This siteShipped
    11. @adrienlcp/biome-config (opens in a new tab)

      The linter and formatter settings every project extends.

      0.1.0Used by TaverlaUsed by on-recordUsed by SéanceUsed by AnalyticsUsed by ScoreboardUsed by This siteShipped

Since 2023 I have been, at work, the sole developer of a multi-screen platform in production. I care about sound architecture, from monorepos to APIs typed end to end, about accessibility and tests, and I keep my projects on the latest versions of their ecosystem.

Your team, 2026

I am looking for my next job. Write to me, or take the CV: a designed one, and a plain one for applicant tracking systems.