RealEstateAI
Workshop

An atlas of the Slovenian coast in which no number is typed by hand

We have put sloveniavillas.com online: a 3D relief drawn with our own shaders, measured driving times, seven calculators and eleven checks that stop the site when it writes something it cannot prove.

8 October 2026 · 8 min

The Gulf of Trieste, the Slovenian coast and the Karst rendered from elevation data as a 3D relief at dusk, with pale contour lines on dark ground and the open sea to the west (a data rendering, not a photo or a generated image).

On 6 October sloveniavillas.com went live: our group's site about the Slovenian coast and the Karst, the places between 18 and 45 minutes' drive from Piazza Unità in Trieste. Four languages, eleven places, guides for buyers and for sellers, seven tools.

One premise explains almost every technical choice. Today we do not broker property in Slovenia: we expect to start during 2027. The site has no homes for sale; it has data. So most of the work went into one specific problem: stopping the site from writing a number it cannot trace back to a source.

No figure typed by hand

The site's texts contain no numbers, only placeholders: one means "minutes from Trieste to Koper", another "median house price in the Slovenian register of property sales". A single function resolves them when the site is built, and every value carries its source and date. A placeholder that does not exist does not leave a hole on the page: it stops the build. A dedicated check rejects figures typed by hand next to a unit, with an explicit list of exceptions (years, dates, legal references).

Even "2027" lives in one place only, with a date by which someone must reread it: after 15 December the site will not build until a person updates it. Even the home page headline is a calculation. In English it takes the nearest place, Sežana at 18.3 minutes, and rounds: "Twenty minutes past Trieste". In German it compares the times from Munich and Vienna, from where Koper turns out to be less than a minute further than Trieste: "Koper liegt eine Minute hinter Triest". If the gap grew, the number would change; beyond nine minutes, so would the sentence.

Minutes, measured

Driving times come from OSRM, the routing engine built on OpenStreetMap data: a single request returns the whole matrix, from ten cities of origin to the eleven places. They are times without traffic, and we say so next to every figure: they usually run 10–20% optimistic, and on the coast in summer they can double.

The wave of minutes spreading across the relief is a grid of 72 by 70 cells snapped to the nearest road, smoothed and then checked against the matrix: across the eleven places the gap stays within 1.6 minutes, except Piran at −4.4, and the documentation says so.

A 3D relief without libraries

The relief comes from open elevation data (EU-DEM and SRTM), about 47 metres per pixel, vertically exaggerated with a legend that says so. The specification called for three.js; the engine does not use it. The scene has six fixed passes with our own WebGL2 shaders, the geometry is built in a Web Worker, and the code weighs about 27 KB compressed.

three.js stayed on as referee. The camera maths is ours, and a script compares it with three.js on about 2,500 random points: the largest gap is 1.2 millionths, and above one ten-thousandth the script fails. If the first 30 frames are slow, the engine lowers the resolution and, if that is not enough, switches to stills of the same scene.

Seven tools, and their error

The tools range from purchase costs compared with Italy, Austria and Germany to the documents needed to sell. Three episodes.

The example price sat right on a boundary. The purchase cost calculator reads the tax rules from a list in which each one has a source and a verification status: an unverified rule is not printed. The Slovenian fee for entry in the land register is banded, and our example price, €600,000, is exactly the limit between two bands. Our code included the upper limit, the Slovenian tariff excludes it: the calculator showed €160 instead of €200. Every table now states its convention.

A formula that ignored the budget. For "find your Slovenia" the specification asked to compare places by the square metres a budget can buy. Normalised between minimum and maximum, though, those square metres produce the same ranking for any budget: the budget cancels out in the division and the cheapest place always wins, even for someone with two million. Instead, the tool now counts how many houses sold in the municipality since 2021 in that price band.

A model with its error in plain view. "What is my home worth" covers Koper, Ankaran, Izola and Piran: a statistical model built on 685 houses and 2,341 flats sold between January 2021 and August 2026, from the public sales register of the Slovenian surveying authority. It is a market analysis for the reader, not an appraisal. The median percentage error, measured out of sample with five-fold cross-validation repeated three times, is 21.3% for houses and 13.0% for flats: one house in two falls outside ±20%. For houses the model does no better than the ratio to the Slovenian assessed value (21.3% against 20.7% on the same sales), so the tool shows both calculations side by side, always as a range. The public file holds only aggregates: a script checks that it contains no prices or locations of individual sales.

Eleven checks, tested on what they must stop

Eleven checks run before every build, from languages to contrast. Each has its own test cases: a small set of files it must reject and one it must let through. The rejected case must fail for the expected reason and that reason only; the accepted case must have read at least as much as it claims, because a check that reads nothing always passes. There are 33 variants, and all of them behave as they should.

  • Banned phrases, language by language. In Slovenian, "posredujemo" (we act as intermediaries) is banned, but "še ne posredujemo" (not yet) must pass, and so must "posredujemo podatke", "we pass on the data".
  • Images. All but one of the 28 images that went through a generative model started from freely licensed photos; each carries the AI mark and a register entry with source, licence, model and changes. Checking by eye sent four back, for invented lettering on signs and people who had been added.

The assistant that cannot see the database

The site has an AI assistant, but the site does not talk to the model. It searches its own texts with BM25, a word index written without libraries, and sends the passages to our CRM with a signature. The CRM does the rest: a switch that starts off, a spending cap, three free questions and, at the fourth, name, contact and consent. The model never sees the database, and any link in the answer that is not in the material sent is removed afterwards.

What does not work yet

  • The home page is too heavy: almost a megabyte of HTML, about 190 KB compressed, against a budget of 100. More than half is data handed to components that run in the browser.
  • We have not yet watched the live relief on a real graphics card. Automated tests use windowless Chrome, which draws in software: the engine notices and switches to stills.
  • German and Slovenian still lack a human read-through: so far, agents have done the reading.
  • Times come from the public OSRM server, not from an instance of our own as the specification asked.

A site that sells nothing has only one asset: that its numbers hold. That is why most of the work is invisible: it is in the checks that stop the build, in the data anyone can download, and in the fact that every check, before we trusted it, was forced to fail.

#data#webgl#fact-checking#tools#sloveniavillas

Keep reading