← Index

Olomouc Quest

August 2026

I turned fragmented Erasmus information into a bilingual PWA that ranks practical actions by residence, faculty and intent — without requiring an account or backend.

The problem was not a lack of information. It was the cost of deciding what mattered now: official guidance, transport, maps and student advice all lived in different places, with different levels of reliability.

I scoped the first version around one job: help a new student reach a useful next action in under a minute. That constraint shaped the information model, the ranking logic and what I deliberately left out.

olomouc2026.pages.dev · live product
Loading live product…
A compact live preview of the product. Open the full app ↗

Product decisions

I used product constraints, rather than a feature list, to keep the MVP small enough to ship and specific enough to test.

Useful immediately

Optimise time-to-first-useful-action, not time spent browsing.

Personal, not invasive

Rank locally from three inputs; store no remote profile.

Local information, verified

Keep changing facts linked to their official source instead of copying them.

The first 72 hours

The homepage does not introduce the whole product. It turns the first 72 hours into six small actions: find the Welcome Office, collect the university card, prepare transport, test the faculty route, locate daily services and connect with other students.

The checklist is deliberately finite. Progress remains visible, is stored on the device and never requires an account. This makes the first session feel like a clear path rather than another directory to explore.

The product trade-off is intentional: breadth loses to sequence. A short checklist gives the first session a measurable completion state and makes it possible to test where students still get stuck.

Personalisation

Olomouc is compact, but the answer to “what is nearby?” changes completely between Neředín and Envelopa. The setup asks only for residence, faculty and one immediate priority. Those inputs feed a client-side ranking function, not a user profile.

INPUTThree choices

Residence, faculty and priority.

LOGICLocal matching

Distance, category and relevance.

OUTPUTA useful next step

Three recommendations, not thirty.

The recommendation system intentionally reduces the amount of information shown.

One source of truth

The useful layer is deciding what deserves to appear on it. I organised dormitories, faculties, supermarkets, canteens, health services, transport stops, cultural places and day trips into a normalised place schema with category, coordinates, source, audience and residence relevance.

Residence guides link back to official Palacký University information and transport actions open the relevant route instead of reproducing schedules that may change1. Spanish and English consume the same objects, so the language switch changes the interface without creating a second product to maintain.

The product loop

Finding a place is only the first step. A student can turn it into a route, add it to a plan, verify the visit with location and keep it in a personal passport.

Visit verification uses Haversine distance between the device and the place radius. The interaction is a product loop — discover, go, remember — but the state remains local to the device.

verified-visit.js3
01navigator.geolocation.getCurrentPosition(position => {02  const distance = haversine(position.coords, place)0304  if (distance <= place.radius) {05    state.visited[place.id] = {06      visitedAt: new Date().toISOString(),07      verified: true,08      distance: Math.round(distance)09    }10    saveState()11  }12})

How it works

The product is a responsive, installable PWA. A service worker caches essential files, while localStorage keeps preferences, checklists and visits on the device.

There is no login, onboarding wall or backend required for the core experience. Location access is optional2, and denying it never blocks the rest of the product.

That architecture reduces hosting and privacy overhead, but it creates a clear limitation: state does not sync between devices. For this MVP I accepted that trade-off because arrival utility mattered more than account continuity.

offline-first.js4
01window.addEventListener("load", () => {02  navigator.serviceWorker.register("./sw.js")03})0405function saveState() {06  localStorage.setItem(07    "olomoucQuestState",08    JSON.stringify(state)09  )10}

What shipped

  • Spanish and English interface
  • Residence and faculty personalisation
  • First-72-hours checklist
  • Curated places and nearby sorting
  • Transport guidance and routes
  • Plans and verified visits
  • Personal city passport
  • Installable offline-first behaviour

Validation plan

I would test the product with new Erasmus students during their actual first week. The primary measure would be time-to-first-useful-action: can someone reach a relevant route, place or checklist item in under sixty seconds without assistance?

Secondary signals would be first-session checklist completion, usefulness of the top-three recommendations and return after day seven. Those results would decide whether to improve ranking, add missing official data or invest in cross-device accounts — in that order.

Footnotes

  1. Recommendations were assembled from first-hand Erasmus context and publicly available local information. Services and schedules should continue to be checked against their official source.
  2. Geolocation is used only when the student explicitly asks to verify a visit. The rest of the application works without granting access.
  3. The verification fragment requests location only after an explicit action and saves a visit only when the calculated distance falls inside the accepted radius.
  4. The persistence fragment registers the installable app shell and serialises preferences and progress as one local JSON state for the next visit.