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

    View projectPlay 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

  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

    View projectOpen 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

  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.

    View projectOpen 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

  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-recordScoreboard

    View projectOpen 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, ON-RECORD AND SCOREBOARDno 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 · four apps report, one Worker writes a row

  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.

    Analytics

    View projectOpen live (opens in a new tab)
    Drawing: the hall’s big screen showing two live tables and the finished ones, and an umpire’s phone scoring table 3 with a button per player and one to undoLIVETABLE 1Henry010Lopez18TABLE 3Roche29Vidal17TABLE 23–1 · finishedTABLE 43–0 · finishedTABLE 5next · 14:30TABLE 3UMPIRERoche9Vidal7+1+1UNDOSAME POINTon every screen at onceEVERY TABLE, ONE SCREENlive matches take the spaceONE TAP A POINTone more to undo it
    Drawn for this register, not a screenshot
    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)

    Mechanism · one log per event, every screen replays it

  6. adrienlcp.com

    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.