Loading
October 26, 2026

AmirHesamTech

Raspberry Pi, homelab, Linux and automation — projects and practical guides

H3samBot, a photography and IT help chatbot that runs in the browser

Most chatbots need a server, an account and an API key that costs money for every question. H3samBot, which I built, needs a browser. It answers questions about photography and IT support from a knowledge base stored as one JSON file, and the matching happens in JavaScript on the page itself. Nothing you type is sent anywhere.

  • Open index.html and it runs. No build step, no npm install, no backend.
  • No API key and no cost, because there’s no AI model call — it’s retrieval, not generation.
  • A knowledge base that refreshes itself every six hours from photography and tech news feeds, using GitHub Actions.
  • Private by design: your questions never leave the browser.

Try it in thirty seconds

git clone https://github.com/amirh3sam/H3samBot.git
cd H3samBot
# then double-click index.html

Ask it something like best shutter for sports or fix high CPU on Windows. A Status line under the chat tells you if anything is wrong, and the same message goes to the browser console.

Because it’s a static site, any file host can serve it. On GitHub Pages: push the files, open Settings → Pages, choose Deploy from a branch, pick main and the root folder, and it goes live at https://<your-username>.github.io/H3samBot/.

Advertisement

How it finds an answer: TF-IDF and cosine similarity

There’s no language model involved. The bot uses TF-IDF with cosine similarity, a classic information-retrieval method — and understanding it explains both its strengths and its limits.

  1. Every entry becomes a bag of words. Its question words and answer are lower-cased, stripped of punctuation and split into terms.
  2. Rare words count for more. That’s the IDF part: a word like “the” appears everywhere and tells you nothing, while “aperture” narrows things down sharply.
  3. Your question is scored the same way.
  4. The closest entry wins, measured by the angle between the two word vectors.

Here’s the core of it from bot.js:

function tokenize(t){
  return (t||"").toLowerCase().replace(/[^a-z0-9s]/g,' ').split(/s+/).filter(Boolean);
}

// inside tfidfVectors(): a word found in few entries gets a high weight
for (const [w, df] of vocab) idf.set(w, Math.log(1 + N/df));

function cosine(a, b){
  let dot = 0, na = 0, nb = 0;
  for (const [w, va] of a){ na += va*va; const vb = b.get(w); if (vb) dot += va*vb; }
  for (const [, vb] of b){ nb += vb*vb; }
  return dot / (Math.sqrt(na) * Math.sqrt(nb) || 1);
}

The practical consequence: it’s very good when your question shares words with an entry, and it has nothing to say when it doesn’t. It can’t reason — but it also can’t invent an answer that isn’t in its knowledge base.

Teaching it something new

Everything the bot knows lives in data/knowledge.json. Add an object to the array and reload the page:

{
  "title": "Shutter speed for sports",
  "q": "sports shutter speed freeze motion fast action blurry football",
  "a": "Start at <b>1/1000s</b> and raise it if motion still blurs. Open the aperture and raise ISO to keep the exposure."
}
Field What it’s for
title A short heading shown with the answer
q The words someone might use when asking — this is what gets matched, so write generously
a The answer; HTML such as <b>, <br> and links is allowed

The q field decides whether an entry is ever found. Put the synonyms in: for an entry about a blurry photo, include blurry, soft, shake, motion and sharp. Entries are missed far more often because of missing words than wrong answers.

Automatic updates with GitHub Actions

A workflow, .github/workflows/update-kb.yml, rebuilds the knowledge base every six hours from sixteen RSS feeds — photography sites such as PetaPixel, DPReview and Fstoppers, and tech sites such as The Verge, Tom’s Hardware and How-To Geek — then commits the new file back to the repository (shortened):

on:
  schedule:
    - cron: "0 */6 * * *"   # every 6 hours
  workflow_dispatch:         # allow manual runs from the Actions tab

permissions:
  contents: write            # lets the job push the updated file

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
      - run: npm ci || npm i
      - run: npm run update:kb
      - name: Commit and Push
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add data/knowledge.json
          git commit -m "Auto-update knowledge.json" || echo "No changes"
          git push

Two details make it work: permissions: contents: write lets the job push, and || echo "No changes" stops the run failing on the days when nothing new was found. You can run the same refresh locally with npm install and npm run update:kb.

Limits worth knowing

  • Vocabulary, not understanding. If your words and the entry’s words don’t overlap, it won’t find the answer, even when it’s there.
  • Answers are trusted HTML. They’re inserted as HTML so they can be formatted, so keep the knowledge base under your own control and don’t paste in entries from sources you don’t trust.
  • English only, as written. The tokenizer keeps only a–z and 0–9, so accented letters and other alphabets are dropped.

Frequently asked questions

Can I connect it to a real AI model later?

Yes, and the design is ready for it. Keep the retrieval exactly as it is, then send the best-matching entries to a model as context along with the question. That’s retrieval-augmented generation (RAG), and the retrieval half is already done.

Does it work offline?

Once the page and knowledge.json have loaded, yes — nothing else is fetched while you chat.

Can I use my own topics?

Replace data/knowledge.json with your own entries in the same shape. Nothing in the matching code is specific to photography or IT.

I also built a chatbot step by step in a three-part series on TikTok. The code for this one: H3samBot on GitHub. Another small, no-build project of mine is the inventory app with Node.js and SQLite.

Advertisement

65 Posts

SDET and QA engineer. I build Raspberry Pi servers, a Proxmox homelab and automations, and share how on TikTok and Instagram (@techwithamirh3sam).

View All Posts

You Missed

REWARDS 🔰 LV1 0🪙 ❯
🔰
Recruit
Guest Operative
Level1
XP0 / 100
💬 Need help?
coins0
Streak0 days · best 0
📋 Daily missions
↻ Resets daily at midnight
Rank ladder
How to earn
✓ Welcome bonus for joining — +200 🪙 & +100 XP (one-time)
✓ Daily check-in (streak bonus) — +20 XP & +12–24 🪙 (more 🪙 the longer your streak)
✓ Spin the daily wheel — +10 to +150 🪙 (free, once a day)
✓ Daily missions — check-in +15 🪙/+10 XP · daily spin +15 🪙/+10 XP
✓ Finish ALL daily missions — +30 🪙 & +20 XP bonus
✓ Level up — +25 🪙 every new level
✓ Achievements — first sign-in +50 🪙 · Lv5 +75 · Lv10 +150 · Lv25 +300 · 7-day streak +100 · 30-day +250 · collector +100 · subscriber +50
✓ Complete your profile (name, bio, photo) — up to +55 XP (one-time)
✓ Subscribe to the newsletter — +250 🪙 (one-time)
Coins and XP are just for fun: they have no cash value and can't be exchanged for money or prizes. You never earn them for viewing or clicking ads, or for how many pages or articles you open.
🛒 Token Shop0 🪙
🛍️ Shop0items
🎒 Inventory0items

🎡 Daily Spin

One free spin every day — pure bonus coins!
🪙
💬Need help?