This is a submission for the
Code
How I Used Gemma 4
Why E2B
The model choice here wasn't a preference — it was a hard constraint that made everything else follow logically.
CrisisLens has to work when the internet doesn't. That eliminates every cloud API. The model has to run on the device a responder is already carrying. That eliminates every model too large for a phone. And it has to work for any responder, not just the ones with a $1200 flagship device.
The Gemma 4 E2B — a 2B effective parameter model built specifically for ultra-mobile and edge deployment — is the only model in the Gemma 4 family that satisfies all three constraints simultaneously. The 31B Dense model requires server-grade hardware. The E4B needs 8GB+ RAM, limiting it to flagship phones. The E2B runs on mid-range Android devices with 4GB+ RAM, and even on hardware as lean as a Raspberry Pi 5.
A rescue worker shouldn't need a $1200 phone to use this tool. E2B makes that true.
And crucially — the vision task here isn't complex reasoning. It's constrained categorization: classify a scene into one of six incident types, assign a severity level, count visible casualties, write a 60-character description. A well-prompted 2B model with a rigid schema handles this reliably. The constraint is the point — by removing open-ended generation and forcing the model into a fixed vocabulary, accuracy becomes a function of prompt engineering, not raw parameter count.
The model isn't a chatbot here. It's a translator — converting heavy visual data into lightweight, actionable intelligence that can physically travel over primitive radio networks.
The Problems I Actually Had to Solve
Building this exposed four problems that were harder than they looked.
Problem 1: Location Was Useless When Generated by the Model
The first version of CrisisLens asked the model to describe the location from the image. The outputs were accurate but useless in context: "urban ruins," "flooded street," "collapsed structure." In a disaster zone, everywhere looks like that. No responder can act on "urban ruins."
The fix was to remove location from the model's responsibilities entirely. The app reads live GPS coordinates from the device at the moment of capture and injects them directly into the payload — the model never touches the loc field. Coordinates like 12.8406,77.6784 are 15 characters, globally unambiguous, and work even when the entire scene looks like rubble. If GPS is unavailable after 10 seconds, the app falls back to "loc":"GPS unavailable" and still processes the image — the analysis is never blocked waiting for a signal.
Problem 2: Getting Gemma 4 to Output Consistent JSON
Gemma 4 is a generative model — it wants to explain, elaborate, and hedge. Ask it to analyze a disaster photo and it might produce a well-written paragraph. Ask it for JSON and it might return JSON wrapped in markdown code fences, or JSON preceded by a sentence, or subtly malformed JSON that breaks the parser downstream.
I solved this in two layers. The system prompt became a formal specification — not "return JSON" but a complete field-by-field schema with types, allowed values, character budgets, and an embedded example of correct output. Second, I added a validation-and-retry loop: if the response doesn't parse cleanly, the app retries up to three times with an increasingly constrained prompt before returning a fallback error state. In practice, the retry loop almost never fires — the schema prompt alone reduced malformed outputs to near zero.
Problem 3: Enforcing 200 Bytes Without Corrupting the Payload
Even with a tight schema prompt, the model occasionally produces valid JSON that runs slightly over 200 bytes. Naive truncation at byte 200 breaks the JSON structure entirely — which is worse than being slightly over limit.
I stopped treating this as a model problem and moved it to post-processing. The schema defines a hard character budget for every field. Critical fields — type, sev, inj, conf — are constrained to enums and integers, so they can never grow. Only desc and act are variable-length. If the total payload exceeds 200 bytes after generation, the app trims desc first, then act, always cutting at word boundaries to keep the text readable. The JSON structure is never touched. The result is always valid, always parseable, always under the limit.
Problem 4: Prompt Engineering for Disaster Scenarios
A generic prompt produces generic output. The first useful breakthrough came from realizing the model needed a closed vocabulary, not open-ended instructions.
Rather than asking Gemma 4 to describe what it sees freely, I gave it a constrained set of incident types (flood, fire, injury, blockage, structural, hazmat) and severity levels (low, med, high, crit). This forced the model to map visual input onto a vocabulary that receiving systems can act on directly — no interpretation needed on the other end. The conf field came from the same principle: if the model is uncertain about what it's seeing, the payload should say so explicitly, so responders can decide whether to verify before acting.
The difference between an open prompt and a constrained one was dramatic. Open prompts produced outputs that were accurate but unpredictable in structure. Constrained prompts produced outputs that were slightly less nuanced but completely reliable — and in an emergency, reliability beats nuance.
SOCIAL SHARE CARD GENERATOR