WildSOS was born from a concrete situation: someone finds a wild animal in distress, does not know what to do and does not know who to turn to. The person there is agitated, often on the phone, often with a low battery. The goal I set myself is clear-cut: a useful answer within 30 seconds of opening the site. Here is how the search that makes it possible works, and where its limits are.

1. Search runs in the browser

The first step is working out which animal it is. The species dataset (over 50) is a local file with name, synonyms, tags and descriptive phrases, and search is fuzzy and client-side with Fuse.js:

const fuse = new Fuse(animali, {
  keys: [
    { name: 'nome', weight: 2 },
    { name: 'sinonimi', weight: 1.5 },
    { name: 'cerca_per', weight: 1.2 },
    { name: 'tag', weight: 1 },
  ],
  threshold: 0.45,
  minMatchCharLength: 3,
  includeScore: true,
})

No server call: results appear as you type and tolerate typos. With the real configuration, “ricio” finds the hedgehog, “gheppo” the kestrel and “pipistrelo” the bat. The weights make the name count more than a synonym and a synonym more than a tag.

2. How non-experts talk

Nobody types “Erinaceus europaeus”. That is why each species has synonyms and a cerca_per field with phrases an ordinary person would use. For example, “animale con le spine” (spiny animal) leads to the hedgehog and “topo volante” (flying mouse) to the bat.

The limit: I wrote these phrases for the most common species, not for all of them. It is the thing I know I must extend.

3. Distance: the simplest formula that is enough

Once the animal is chosen, I show the nearest centres. Distance is as the crow flies, with the Haversine formula:

export function haversineKm(lat1: number, lng1: number, lat2: number, lng2: number): number {
  const R = 6371
  const toRad = (deg: number) => (deg * Math.PI) / 180
  const dLat = toRad(lat2 - lat1)
  const dLng = toRad(lng2 - lng1)
  const a =
    Math.sin(dLat / 2) ** 2 +
    Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2
  return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
}

I do not use a routing service: it would need a key, it would cost money and it would not change the decision (“call the nearest centre”). I state the limit: straight-line distance is not driving time.

4. The nearest centre is not always the right one

A centre a few kilometres away that does not handle birds of prey is no use to someone who found a falcon. Each animal has a category (raptor, bird, mammal, reptile, amphibian) and each centre declares the species it handles. I compare the category against a list of keywords and put the centres that handle that category first, in order of distance, then all the others.

Two choices matter: I never exclude centres without a match (if none matches I show the full list), and the match is a text comparison, not an ontology: it is crude, but robust to how centres write their species.

5. When there is no location

The browser may refuse geolocation, or not have it. The request has a 10-second timeout and, if it does not succeed, a field appears to type a city or postcode, which I turn into coordinates with OpenStreetMap (Nominatim). It is a choice consistent with the rest of the project: maps with Leaflet and OpenStreetMap, no paid services. One privacy point must be stated: the text you type in the city field is sent to that service.

6. A link that pre-fills the search

Animal cards on the home page lead to /emergenza?q=riccio, and the search starts already filled in. The Telegram bot uses the same logic (fuzzy search over animals and nearby centres), with no extra dependencies.

Limits I state

  • Fuzzy search is permissive. With short words it can suggest species that look alike by letters: that is why I show up to six results and let the person choose.
  • Distance is straight-line, not driving time.
  • Centre data must be verified. That is why a centre’s page says when it has confirmed its own data.

What I learned

  • In an emergency people scan, they do not read: a single main action and immediate answers beat any extra feature.
  • Doing the work in the browser removes latency and points of failure.
  • Stating the limits is part of the product.