FIVB · sustainability of volleyball events · live since May 2026
Planet Positive Sport
The platform FIVB uses to measure, review and certify the environmental footprint of volleyball events, with reports rendered by the server and AI run on our own GPU.
623
Questions in 14 categories that FIVB maintains in its own editor, with no developer
63
Days from the first commit to production
20
Events in 12 countries on 4 continents by 22 July 2026
One frozen snapshot, three reports
When an admin clicks Generate, the platform freezes the event's numbers into one record that later edits cannot touch. Three things are built from that snapshot: the public web page, a PDF laid out like a printed report, and a single HTML file that carries its own fonts, script and photos. The public page costs one database read.
Publishing has two guards. Generate stays blocked while the event's answers are newer than the draft, and also while any federation setting that moves results is newer, from carbon figures to the questionnaire itself. The same check runs again inside the final database transaction, so a draft that changed while the PDF was rendering is refused. If either file fails to render, nothing is published.
Each Generate adds a numbered version and retires the one before it, and the public link never changes. The numbers stay frozen while the design stays live. A layout fix reaches reports already published on the web, and the PDF and the HTML file stay exactly as issued. An admin's download and a visitor's are the same stored file, byte for byte.
The report has nine charts of six types, and no chart library draws them. The score ring and the attendee donut are circle arcs in SVG, which every browser can draw. The category radar is an SVG polygon. Bars are plain HTML blocks sized by the server, and the A to H carbon label is a ladder of chevrons cut with CSS. The browser gets two hand-written scripts, 606 lines in all. The report page runs only one of them. Its 324 lines replay the entrance animation and skip it for readers who asked for less motion. The PDF runs no script, so it always prints complete.
- One frozen record behind the web page, the PDF and the HTML file
- Every version kept, and a public link that never changes
- Generate blocked while any input is newer than the draft
- Review hints such as data-gap markers appear only on the reviewing admin's screen
- Each report prints the scoring version that produced its numbers
A sustainability report that watches its own weight
Photos make up most of a report's weight, and committees upload camera originals. On one real report, 62 action photos shown 89 pixels wide were going out at full size. The worst had 60 times the pixels it needed. Every image now gets a 1200-pixel copy, and only the full-width cover gets a 2560-pixel one. Photos per page view fell from 140.5 MB to about 19 MB, and a second visit on the same day downloads zero image bytes.
Lighthouse is Google's audit of page speed. In a local mobile run the public page went from 68 to 98, and the first text now appears after 0.9 seconds, down from 5.0. The cause was 69 photos embedded in the HTML itself, which made up 78% of it. The web page now links them as files the browser can keep.
Print needed its own fix. The web page uses compact WebP images. Chrome's PDF engine keeps JPEG photos as they are and turns every WebP photo into a far larger lossless copy, so one 146 KB section photo became 2.6 MB inside the PDF. The print version now uses JPEG copies of the section photos. One real report went from 10.8 MB to 2.4 MB with the same 39 pages. The PDF render also makes zero network requests, because Chrome asks for each picture and the server hands it over from memory.
The downloadable HTML file re-encodes each photo as WebP at twice the width it is shown at: 1600 pixels for the cover, 800 for venue photos and 400 for action photos. On a test set of 69 photos it came to 5.89 MB. Embedding the originals gave 26.15 MB.
- Photos per page view down from 140.5 MB to about 19 MB
- Mobile Lighthouse from 68 to 98 in a local run, with the first text at 0.9 seconds
- A real report's PDF down from 10.8 MB to 2.4 MB, with the same 39 pages
- After the speed work, 42 PDF pages compared pixel by pixel showed zero changes
- A note printed only in the PDF asks the reader to consider not printing it
The AI runs on our own GPU and may leave a goal out
The AI drafts two parts of the report. It maps each objective the committee wrote to up to three of the 17 UN Sustainable Development Goals, and it writes a short paragraph beside the performance figures. The model is Qwen3 14B, running on one 20 GB graphics card in JT Digital's GPU server in Germany. The platform reaches it through JT Digital's own message queue, and there is no per-token bill.
Every call runs at temperature 0, which makes the model pick its most likely answer each time. The model may leave a goal out when it is unsure, because an empty mapping is a valid answer on a certified report. The paragraph is told to use only the numbers it was given and never to compute its own. Committee text goes in fenced as data, with an instruction to ignore any orders written inside it.
Code checks every answer before use. Goal numbers must fall between 1 and 17, with at most three per objective, and each must point at an objective that exists. An invalid answer is retried twice, and the job gets three attempts. A person reviews the draft, and the platform will not publish until the AI step has finished.
The report keeps its numbers if the AI fails. They are saved as a draft before the model runs, so a failed step still leaves a reviewable report with every figure in it, and the admin gets a Retry button.
- Qwen3 14B on one GPU card in Germany, reached through our own message queue
- Temperature 0 and a fixed answer format, checked in code
- An empty goal mapping counts as a valid answer
- Objectives sent as short numbers, which made the goal-mapping prompt 30% shorter
- Every figure survives an AI failure
623 questions that FIVB maintains without a developer
FIVB's questionnaire has 623 questions in 14 categories. Of those, 127 are scored and 496 collect data such as kilowatt-hours, kilometres and hotel room nights. All of it is built and maintained in an editor in the browser, and a change needs no code release. Every save is a new revision. When two admins edit at once, the older save is refused and the editor reloads, so neither overwrites the other.
An emission factor is the number that turns an amount, such as litres of diesel, into kilograms of CO2e (carbon dioxide equivalent). An answer can use a fixed factor, or let another answer choose it, so the fuel type decides between diesel and petrol. It can also be multiplied by another answer, such as streaming hours times viewers. The catalogue holds 416 factors in 9 calculation blocks, from energy and mobility to catering and hotels. FIVB edits them in a side panel, each factor keeps a value per year, and any edit marks every draft report out of date.
Deploying an edited questionnaire over live events is the risky step, so it runs 70 checks first, and 66 of them can block it. Before anything applies, the platform lists what would be lost, from deleted questions to answered ones whose type would change, and asks for confirmation. A chain of factors with no carbon factor in it is refused outright, since its result would not be kilograms of CO2e. Questions are matched by stable keys, so existing answers stay attached.
A new federation gets its own address on the platform, with working HTTPS, from one form. The platform creates the address and installs its security certificate straight away. The admin sets the name, two brand colours, the logos and up to four partner marks. Access runs on 5 roles and 30 permission codes, and a misspelled code stops the build. The interface, the emails and the carbon label come in five languages: English, Japanese, Portuguese, Thai and Chinese. The report itself is written in English.
One thing stays out of the editor on purpose. The scoring formula lives in code, under a named version that a test guards.
- 623 questions, 127 of them scored and 496 collecting data
- 70 checks before a questionnaire deploy, 66 of them blocking
- 416 emission factors in 9 calculation blocks, each with a value per year
- Address, certificate, colours and logos for a new federation from one form
- 5 roles and 30 permission codes, 5 of them granted per person
A hundredth of a point decides certification
An event's score runs from 0.00 to 5.00 and has three parts: the questionnaire, the expert's score for each action item, and an A to H carbon label. An event qualifies at 2.61, so rounding can decide the verdict.
The carbon total comes from a pure function, meaning it sees nothing but the data it is handed. A loader fetches everything an event needs in at most six database queries, however many questions it has. The calculation then runs with no database, no clock and no hidden state. Hotel nights are priced by where the hotel is. The calculator tries the hotel's city, then the event's country, then one of nine world regions, then a world figure, and a test fails if a new country is missing from the region map.
The total also says when it may be too low. A missing factor, an unanswered linked question or an answer that is not a number sets an incomplete flag. A malformed row that still produced a value adds a warning and leaves the flag alone. The flag reaches the admin's review sheet and tells the AI to hedge its wording.
Scores keep full precision through every step and round to two decimals once, at the end. A unit test feeds the same inputs through both rules. The retired rule rounded at three steps and lands on 2.61, which qualifies. Rounding once lands on 2.60, which does not. The value that prints is the value that qualifies.
Every weight, threshold and rounding step is written out as a named snapshot for the current methodology version, 15 rules in all. A test compares the live code with that snapshot. Change a rule without declaring a new version and the test fails, naming the value that moved, its old reading and its new one. Fixed inputs also go through the real functions, so a change in rounding order is caught even when every constant stays put. A script scores every event under the old and the new formula and lists any that would cross 2.61.
- At most six queries, then pure maths with no database or clock
- Hotel nights priced by city, then country, then region, then world
- A flag on any total that may understate the footprint
- One rounding step, at the end, with a test at 2.61 that shows why
- 15 scoring rules held in a named snapshot that a test checks
Live in 63 days, then on four continents
The first commit landed on 16 March 2026 and the first production deploy on 18 May, 63 days later. By 23 September the main branch held 1,278 commits and 3,988 test cases, and production had taken 59 deploys.
By 22 July, 20 events in 12 countries on 4 continents were being assessed on the platform, indoor and beach. Between them, 29 organising committees logged 2,764 data points, and suppliers sent 199 submissions.
Part of that data comes from people who will never sign up. A hotel or a caterer gets a link by email that opens a form already filled in with the event's details, in its own language. It answers with no account, and an admin approves what it sends before any figure counts. When the committee submits, an independent expert reads the same screens with editing switched off, scores the action items and signs off one category at a time. Final confirmation waits until every category is signed and no blocking comment is open.
- 63 days from the first commit to production
- 59 production deploys and 3,988 test cases by 23 September 2026
- 20 events in 12 countries on 4 continents by 22 July 2026
- 199 supplier submissions and zero supplier accounts
- Expert sign-off one category at a time
More work

GZS Inovacije
Slovenia's national innovation awards, from the regional round in thirteen chambers to the national ceremony, on one platform we built and still run.
Every one of the 235 entries got a branded 35-second video for the ceremonies, made from the text of its own entry with a Slovenian voiceover. Nobody edited any of them. Technical terms are checked against Slovenia's official lexical database, and the narration is fitted to each video's length without a change anyone can hear.

Olimpijski Kviz
A quiz played on ten thousand phones at once, in step with a live Winter Olympics broadcast, every night for sixteen nights.
Every phone asks the server what it should show, about once a second, over ordinary HTTP. There are no WebSockets anywhere. A single clock drives the quiz through lobby, question, sponsor break and standings, and the answers come from a cache prepared for the phase about to start.

fluent-html
Our TypeScript library that writes HTML as function calls, and the rendering layer under every web app we ship.
When two files disagree about the name of an element, the page stops compiling. An HTMX swap aimed at an id the response does not contain used to break the page silently in the browser. Now the compiler names both ids before the code ever runs.
Want something like this?
Tell us what you need and we build a working prototype first, so you see it before you pay for it.