devto 2026-10-14 원문 보기 ↗
This is a submission for the Hacktoberfest Open-Source AI Challenge: Week 2 (Side Hustle).
Live demo: https://plotkeep-pi.vercel.app/studio
Code: https://github.com/sherotree/plotkeep
Prize category: Best Use of Gemma.
Plotkeep is a static web app that turns a pasted table into a chart without sending the table anywhere.
You paste CSV, TSV, or cells copied from a spreadsheet — or a sentence if you only have the shape of the question. The app:
$4,200, European decimals, tab-separated paste).value, the error goes back to the model (up to three tries). After that, a deterministic heuristic draws anyway.make it a pie, title to "June collected").The product name in the brief was ChartLocal. I called it Plotkeep because the job is not “charts, but local” — it is keeping the plate on the desk.
There is no account, no Plotkeep API, and no telemetry on the CSV. The only network the studio uses, if you opt in, is the first download of Gemma weights (WebLLM / Hugging Face) or a call to 127.0.0.1:11434 if you already run Ollama.
A freelance operator who already bought a laptop with a GPU, who pastes client names and invoice totals into a chart generator, and who has noticed that “paste your CSV” often means “POST your CSV.”
That is a real irritation even if I am not going to invent a user interview. Aging reports, hours-by-client, a cafe’s Saturday SKUs — the sheet is small, the names are not public, and a $12/month hosted plotter is the wrong object. The silicon is already on the desk. Charging monthly for tokens the GPU can spend locally is a weird shape.
So the license idea is $29 once, per operator, extra personal machines allowed. This repo does not take payment. The Pro surfaces (saved plates in localStorage, 2×/3× PNG) unlock with the demo code PLOTKEEP-PRO so the paid edge is visible, not vapor. Free forever includes the heuristic engine, local Gemma, Ollama, and 1× export.
I am not claiming conversion rates. I am claiming the billing unit matches the cost structure: you pay for a press, not for inference.
Live: plotkeep-pi.vercel.app/studio. Repo: sherotree/plotkeep. To run locally:
npm install
npm run dev
Open /studio. The invoice sample is preloaded. Draw chart works with no model — that is deliberate. On this build it suggested a three-series line (invoiced / collected / overdue by month) and wrote “May leads invoiced at 6,100; Feb is the low at 3,100.” Load Gemma only if the browser has WebGPU and you can wait out a ~1.35 GB first fetch (cached after that). Screenshots of that flow live in docs/screenshots.
The screenshots below are real runs of Gemma 2 2B IT (gemma-2-2b-it-q4f16_1-MLC-1k) loaded in-tab via WebLLM, in Chrome on a Mac mini.
In my first round of testing, Gemma 2 2B misread the freelancer CSV. Asked "which client grew fastest?", it said "Client B grew the most, increasing from 1800 to 2600." That was wrong: client_c went from 900 to 4100, and client_b ended at 3100, not 2600. It also plotted only client_b.
So Plotkeep no longer trusts the model with arithmetic. For a parsed table it now computes the stats on device, in plain code: first and last value, min and max with their labels, absolute and percent change for each series, which series grew fastest, and the best and worst period. Those facts go into the Gemma prompt as ground truth. Gemma chooses the chart type and encoding and writes the wording. Every numeric series is plotted unless you ask for specific ones. After the model replies, a check compares the insight with the computed facts. If it credits the wrong series, names the wrong peak, or quotes a number that isn't in the table or the stats, the insight is replaced with a sentence built from the facts. The status line shows "facts checked on device" either way. In the reshoot above, Gemma's grounded answer passed the check: "Client C experienced the most significant growth, with a 355.6% increase over the period."
If you are on a machine without WebGPU, stay on the heuristic engine or point Ollama at gemma2:2b. The UI says which of those is true; it does not pretend a cloud fallback exists.
Stack: Vite, React, TypeScript, Zod, Apache ECharts, @mlc-ai/web-llm. Gemma 2 2B IT is the default WebLLM id (gemma-2-2b-it-q4f16_1-MLC-1k); Gemma 3 1B is on the menu for smaller VRAM. Gemma 2 has no system role, so instructions are folded into the first user turn.
The useful engineering is not the prompt. It is treating the model as an untrusted spec compiler:
type, x, y, optional series, title, insight. The table is not echoed back through the model. That keeps numbers honest and tokens short.extractJson peels that. Trailing commas get stripped. The next user turn is the Zod error, not a rewritten essay.COOP/COEP for SharedArrayBuffer; Vite, vercel.json, and public/_headers set them. Weights cache in the browser. The heuristic path never needed a network.I used ECharts instead of Vega-Lite because PNG/SVG export from a plate is a one-liner and the default dark plate looked like letterpress ink rather than a dashboard widget. That is a taste choice, not a moral one.
What is still thin: I did not fine-tune Gemma. The 1k-context build will choke if you paste a 200-column dump — the parser will accept it, the prompt truncates to 20 rows, and the spec may pick the wrong y. Pro is a localStorage flag, not Stripe. Ollama from a static host still needs OLLAMA_ORIGINS. Those are limits, not a backlog dressed up as vision.
A hosted chart-from-CSV feature is easy, and it is also a copy of the sheet on someone else’s GPU. For this job that is the whole product risk.
Open weights change the economics in four concrete ways:
gemma2:2b. If a better open-weight JSON-follower shows up in prebuiltAppConfig, it is a dropdown.Gemma is the right default for this challenge because it is small enough to contemplate in a tab (2B, or 1B if the laptop is modest) and instruction-tuned enough to emit a short JSON object when you show it the schema and an example. It is not reliable enough to skip validation. Plotkeep’s bet is that validation plus a local model is the product, not a chat window with a screenshot of a chart.
If you bill by the hour and you are tired of uploading the hour sheet to draw a bar, the press is at /studio.