This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle
A lecture, a system sketch, or a photo of a whiteboard should become a diagram without a closed API ever seeing the notes.
What I Built
Inkline is a desk that turns notes into a diagram.
Paste lecture notes and you get a mind map. Paste “a client just asked for one more thing” and you get the decision as a flowchart. Describe a link shortener, an invoice, a launch week, or the service you are about to build, and you get an architecture diagram, a sequence, or a timeline. Drop a photo of a whiteboard or a page of handwritten notes and Gemma reads the picture. Under the drawing, “Change it…” takes a sentence like “add a Redis cache between API and DB” and updates the diagram in place, with undo, redo, and a short history. Simpler, Balanced, and Detailed change how much of the notes makes it onto the page. An open-weight model, Gemma 4 through Ollama, writes the Mermaid. The page draws it. You copy the source, copy a Markdown block for Notion, Obsidian, or GitHub, export SVG, PNG, or a vector PDF, or share a short link that opens the same drawing.
A switch at the top chooses where that model runs. Private, with a lock, means localhost Ollama: the notes stay on the machine. Cloud means Ollama Cloud. The server picks the provider from the request, so the same desk can do both. If Private is selected and Ollama is not running, the page says to start ollama serve and pull gemma4, instead of failing in silence.
Who would pay. The people already doing this by hand:
- Students who rewrite a chapter as boxes the night before an exam.
- Developers who have to explain a system and would rather not spend half an hour fighting Mermaid syntax.
- Freelancers and small teams who drop a diagram into a proposal, a README, or a change order.
Running it yourself is free. That is the point of an open model: the notes never have to leave the laptop, and there is no per-diagram bill. Themes, picture input, edits, history, and export are in that free desk. The thing I would charge for is the hosted desk, for someone who wants it in a browser and does not want to install Ollama. ₹199 a month, or $5 a month, for Cloud Gemma, higher limits, picture-to-diagram, and the history of a drawing. A team plan would be one shared Cloud budget for a lab or a small group. The pricing page in the app shows those three plans. It does not take a card. Pro and Team are not on sale.
The tedious part is not the idea. It is getting a model to emit Mermaid that actually parses. Small open models are good at the shape of a system and bad at the punctuation. Inkline’s whole trick is a repair loop. If Mermaid rejects a draft, the parser error goes back to Gemma, and it tries again, up to three times. The drafts sit on the page, rejected ones included, so you can see what broke.
Demo
Live desk: https://inkline-lzyf.onrender.com
Six drawings Gemma made from real notes are in the gallery. Each link opens that drawing on the desk.
Code
Repo: InKline
The repair loop lives in src/lib/repair.ts, and the desk is one Next.js page.
How I Built It
Gemma 4 is the only model. There is no closed-model fallback.
Locally, the Next.js server talks to Ollama at http://127.0.0.1:11434, model gemma4. Cloud requests go to https://ollama.com with OLLAMA_API_KEY, usually gemma4:31b. The desk sends provider: "private" or provider: "cloud" on each draw, and the server resolves that to a base URL. A private request never attaches the cloud key, and the key is not in the client bundle or the health check. The same client speaks Ollama’s OpenAI-compatible /v1/chat/completions endpoint, and falls back to Ollama’s own /api/chat if that path is missing. Pictures use the images field on /api/chat, because Gemma 4 is multimodal. If that call is rejected, Inkline asks Gemma to describe the picture in words and then runs the normal repair loop on the description, and the page says that is what happened.
A picture is read once. Gemma lists the boxes and the arrows, and the desk shows that list as “Here's what I saw”: one row per arrow, with from, to, and a label. You can fix a row, add one, or remove one, and then press Draw. The drawing is made from those rows, not from a second look at the photo, so a wrong arrowhead is something you can see and correct before it becomes a diagram. The repair loop then rejects a draft whose arrows do not match the rows you confirmed. A chat edit sends the current Mermaid, the original notes, and the instruction through the same loop. Detail is a clause in the prompt: Simpler stays under about 8 nodes, Detailed keeps the secondary facts and stays under 24, and neither is allowed to invent facts that are not in the notes. The first draw from a picture stays Balanced, so the arrow check is not also fighting a detail edit. OLLAMA_REASONING_EFFORT=none keeps a thinking model from spending the whole reply on a thought trace and returning no diagram.
The prompt asks for one Mermaid fence and a specific diagram shape. The reply is rarely that clean, so extraction pulls the fence out of prose, generic code blocks, and Gemma’s thinking wrappers, and straightens curly quotes.
Then Mermaid parses it on the server. happy-dom is there because the parser expects a DOM. If parsing throws, that error string — the line, the token it wanted, the token it got — becomes the next user message, next to the broken source. The loop stops at three drafts, or sooner if one parses. The API streams each draft as it lands, so the page can show “Draft 1 was rejected” while Gemma is already on draft 2. If the browser’s Mermaid still disagrees with the server, that render error goes back too, using whatever attempts are left. A run that fails all three drafts leaves the previous drawing on the page. The rejected drafts stay in the rail so you can open them.
The page is Next.js, TypeScript, and Tailwind. Diagram types are auto, mind map, flowchart, sequence, architecture, and timeline. Architecture is drawn as a flowchart with subgraphs, because that syntax survives a small model better than architecture-beta. A share link is a compressed hash (lz-string) of the type, the Mermaid, and the edit history, plus the notes when they still make the link shorter. PDF export traces the SVG with svg2pdf, so the file is vectors rather than a JPEG stuffed into a page. The Paper theme’s sample drawing is a baked SVG, so the first paint does not boot Mermaid.
Tests mock the model. They check extraction, that a parser error is actually present in the follow-up prompt, that the loop stops at three, and that a dropped connection keeps the drafts already made. Playwright walks the desk twice, at a desktop width and at 375px, against that same mock: generate, edit and undo, keyboard use, export, the picture review, and the empty desk.
Limits, said plainly.
- The public demo allows 10 draws a minute and 50 a day for an IP address, and 200 Cloud draws a day for the whole service (
DAILY_CLOUD_LIMIT). Private draws do not count against that Cloud cap. Without Upstash, those counters live in the server’s memory and reset when the process does. - Notes cap at 20,000 characters. An edit is 500 characters. A picture is 4 MB, PNG, JPEG, WebP, or GIF. The model reply is capped around 2,500 tokens.
- A hidden company field and a same-origin check sit in front of the draw API. They are speed bumps, not a login.
- Detailed cannot add a service you did not mention. Simpler will drop a branch you might have wanted.
- The PDF is one page. A very wide map is scaled to fit, not split.
- Pro and Team are display only. Nothing on the pricing page charges a card.
- Gallery previews are drawn in the browser. If Mermaid fails there, the link to the desk still works.
- A picture is only as right as the arrow list you confirm. Draw uses those rows. It does not look at the photo again.
Accuracy note. The sample whiteboard was redrawn with gaps between the boxes and large arrowheads. The ink is Browser → CDN (HTTPS), CDN → API, API → Redis (cache), and API → Postgres (SQL). On 17 Oct 2026, three reads with gemma4:31b each returned those four directions and those labels. That is 3 of 3. The check stays on the page anyway: Draw uses the rows you confirm, and a wrong row is what gets drawn.
Render hosts the hosted desk. render.yaml points the service at Ollama Cloud, because Render has no local GPU. GET /api/health is the health check. Per-IP and daily rate limits keep a public URL from burning through the Ollama key, and the key never leaves the server.
Why Does Open Innovation Matter?
A diagram tool that sends every note to a closed API is a bad deal for the people this is for.
A student mapping a chapter does not want that chapter stored on someone else’s account. A freelancer sketching a client’s system is holding someone else’s architecture. Private is the default, with a lock on the switch: localhost Ollama, and the prompt stays on the machine. Cloud is a deliberate second position for someone who has not installed Ollama, using the same open model. A whiteboard photo and a handwritten page take the same path. The picture is another kind of note, not a reason to call a closed vision API.
Cost is the other half. The hosted price only works if inference is close to free. Gemma is open weight, so a month of diagrams does not depend on a closed model’s token price going up. I can swap gemma4:e2b on a laptop for gemma4:31b in the cloud, or a later fine-tune aimed at Mermaid, by changing OLLAMA_MODEL. The repair loop is the same either way. A closed API would charge for the failed drafts too, and I would not be allowed to tune the model when it keeps making the same bracket mistake.
Open is what makes the side hustle possible: private by default, cheap enough to run, and not stuck on one vendor’s model.
Prize Categories
-
Best Use of Gemma. Gemma 4, through Ollama, writes every diagram: mind maps, flowcharts, sequences, timelines, and architecture, including the picture path. Local tags (
gemma4,gemma4:e2b,gemma4:e4b) and Ollama Cloud (gemma4:31b) are the same client. No closed model is called. -
Best Use of Render.
render.yamldeploys the Next.js desk. The hosted service uses Ollama Cloud because Render has no local GPU.GET /api/healthis the health check. - Best Use of a .tech Domain. The live desk runs at [ADD .TECH LINK], pointed at the Render service. <!-- delete this bullet if no .tech domain -->


Top comments (0)