FELN WebGPU: A Language Model, a Database, and a Map in One Browser Tab

I have been exploring how small language models can turn a plain-English question into a useful GIS query. With FELN LoRA, that meant training a model on a GPU and running it locally on my Mac. For feln-webgpu, I wanted to bring the language model, the database, and the map into the web application itself.

Type “Give me all oil discoveries,” and the browser generates a query plan, compiles it to SQL, executes it, and draws the results. The example in the repository finds 562 discoveries, with 200 displayed. We can inspect the plan and SQL beside the map. I like being able to see what the model actually asked the database!

North Sea Query showing oil discoveries on a map, the generated FELN plan and DuckDB SQL, and a table reporting 562 matches with 200 displayed.
The question, query plan, SQL, and resulting features in one browser tab.

The pieces are a fine-tuned LiquidAI LFM2-350M model, Transformers.js running on WebGPU, DuckDB-Wasm with its spatial extension, and the ArcGIS Maps SDK for JavaScript. Once the assets have loaded, query generation and database execution happen on the user's machine. There is no inference server or database server behind the interaction.

There is an initial download: 443 MB for the exported model weights and 5.9 MB of Parquet data for wells, discoveries, and pipelines. The application also fetches libraries, the spatial extension, and basemap tiles from external services. Local query processing is useful, but this version still has network dependencies.

For the oil-discoveries example, the model produces:

{
  "layers": ["Discoveries"],
  "where": ["discovery_type = cast(3 as INTEGER)"],
  "relations": []
}

A JavaScript port of the FELN compiler turns that plan into DuckDB SQL. DuckDB reads the geometry from the Parquet tables, and the application sends the returned features to an ArcGIS graphics layer. The model's responsibility is translating the request into FELN. The database performs the selection.

Getting a 350-million-parameter model to do that required attention to the examples. An early version saw “water depth” during training, but a question using just “depth” could make it invent a column. Adding paraphrases addressed that gap. The current preparation also includes more natural wording and combined constraints, while keeping a query plan and all its wordings on the same side of the training/validation split.

This project uses full fine-tuning. The latest 350M run took about 31 minutes on one RTX PRO 6000. Its system prompt includes the OKF catalog documentation, roughly 10,900 tokens describing the data. That is a substantial amount of context for a short question.

The useful trick is to process that common prompt once. At startup, the application spends about seven seconds building a prefix cache, then reuses the model state for subsequent questions. In the recorded comparison, this brought generation from about seven seconds per question to 0.7–1.3 seconds. A separate 210-question browser holdout run averaged 0.46 seconds for generation. Those measurements exclude downloading the model and drawing the result.

The export experiments also helped choose the download size. On the earlier 81-question browser evaluation, FP16 and the 8-bit export both matched 74 expected plans. The 8-bit version reduced the weights from 692 MB to 443 MB. The smaller 4-bit exports lost accuracy, so the application stayed with 8-bit weights.

For the current v4 model, the recorded browser results are:

MeasurementResult
Exact FELN plans203/210 (96.7%)
Matching result sets208/210 (99.0%)
Mean generation time0.46 seconds

Matching rows and matching plans answer different questions. Two different filters can happen to select the same records in this dataset. I want to keep both measurements visible. A larger 1.2B model reached 204/210 exact plans in the Python evaluation, one more than the 350M model, and was not exported into the application.

There is a spatial issue to fix before relying on distance queries: the current compiler passes metre-valued thresholds to ST_DWithin while the geometry is stored in WGS84 degrees. Those units do not match. The browser reproduces the Python implementation, so agreement between them does not establish correct distances. The correction belongs in the shared compiler and then in the browser port.

What interests me here is how much of this workflow fits in a browser tab: language, structured queries, a spatial database, and a map. The dataset is small, the model still makes mistakes, and the initial load matters. Even with those limits, this is an exciting direction for focused GIS applications.

The code and evaluation tables are available on GitHub in feln-webgpu. I would like to keep pushing on natural wording and spatial correctness as this develops. More to come!

Comments

Popular posts from this blog

How to use Esri Flex API on Android and iPhone

Weighted Map Feature Clustering With Attributes

Augmented Reality, iPhone and ArcGIS Server