DEV Community

SEN LLC
SEN LLC

Posted on

100 Projects in 4 Days — A Meta-Dashboard Celebrating the Milestone

100 Projects in 4 Days — A Meta-Dashboard Celebrating the Milestone

Four days ago (April 10, 2026), I started publishing entry #1: a cron timezone viewer. Today, entry #100 — this meta-dashboard — closes the loop by reading data.json at runtime and analyzing the 100 projects themselves. Every entry is vanilla JS, zero dependencies, with tests. The total test count is in the thousands. The total lines of code is in the tens of thousands. All of it public.

This is the 100th entry in the SEN LLC 100+ portfolio series. It's a self-referential dashboard: it fetches the portfolio's own data file and renders statistics about the 100 projects. Categories, tech stacks, timelines, tag clouds, an entry explorer. The point is to make the scale visible.

🔗 Live demo: https://sen.ltd/portfolio/portfolio-stats/
📦 GitHub: https://github.com/sen-ltd/portfolio-stats

Screenshot

Features:

  • Fetches https://sen.ltd/portfolio/data.json at runtime
  • Headline stats (total entries, total tests, total categories, average tests)
  • Bar charts for categories, tech stacks, stages (CSS-only)
  • Timeline chart (projects per month)
  • Top-tested projects ranking
  • Tag cloud
  • Filterable/searchable entry explorer
  • Bilingual celebration hero
  • Zero dependencies, 32 tests

The numbers

Metric Value
Total entries 100
Total tests written ~4,500
Categories 7
Tech stacks 20+
Average tests per entry ~45
Dependencies in any project 0
Build steps in any project 0

Every single entry follows the same pattern: vanilla JS ES modules, node --test for testing, python3 -m http.server for local serving. No npm install. No webpack. No TypeScript. No framework. The "comparison series" entries (021-030) intentionally broke this rule to compare React / Vue / Svelte / Solid / Nuxt / SvelteKit / Qwik / Astro / Lit / Preact head-to-head — but those are the exception.

What I learned building 100 things in 4 days

1. Consistency beats cleverness

Every entry has the same file structure (src/, tests/, package.json, LICENSE, README.md). Every entry has a npm test script. Every entry has bilingual UI. The consistency made each one faster to build than the last, because the template was settled.

2. Zero dependencies is liberating

Not having a build step means every entry works forever. No security updates. No framework deprecations. No "can't install dependency X anymore". You can git clone any of these repos in 5 years and they'll still work exactly the same.

3. Japanese + English from day one

Bilingual UI was non-negotiable. It made the i18n pattern a first-class concern, which forced cleaner separation of content from code. Every entry has an i18n.js module. The total number of translation strings across all 100 entries is probably 5000+.

4. Tests as documentation

Every entry has a tests/*.test.js file with at least 15-20 tests. Reading the tests often tells you what the module does faster than reading the source. Some complex entries (curl-converter, ts-errors-jp, aws-services-ref, color-picker-pro) have 60-100+ tests.

5. Category breakdown

Roughly:

  • Developer tools (40+): cron tools, regex, JSON/YAML, text encoding, hex viewer, etc.
  • Games (10+): 2048, sudoku, minesweeper, snake, wordle-jp, memory, gomoku-ai, mahjong, etc.
  • Design tools (10+): shadow designer, gradient designer, color picker pro, Perlin noise, fractals, pixel art editor, etc.
  • Japanese culture (10+): era converter, kansuji, kana converter, hiragana-romaji, lorem-jp, wordle-jp, etc.
  • Calculators (10+): warikan, shohizei, BMI, sleep cycle, tip, loan, recipe scale, etc.
  • References (10+): HTTP status codes, AWS services, Git cheatsheet, CSS props, Linux signals, TS errors, etc.
  • Daily utility (10+): cook timer, pomodoro, kanban, shopping list, countdown days, mind map, lifework clock, etc.

This dashboard's own math

export function totalTests(entries) {
  return entries.reduce((sum, e) => sum + (e.testCount || 0), 0);
}

export function countByCategory(entries) {
  const map = new Map();
  for (const e of entries) {
    map.set(e.category, (map.get(e.category) || 0) + 1);
  }
  return map;
}
Enter fullscreen mode Exit fullscreen mode

Pure functions operating on the loaded entries array. The main.js fetches data.json, passes it to these functions, and renders the results. No state, no side effects beyond DOM updates.

What's next

100 was the goal. Goals past 100 haven't been set yet. But looking at the docs/ideas folder there are still dozens of unimplemented ideas — graphics experiments, more games, API integrations, reference sites. The pattern is set; new entries are faster to build. 200 is plausible.

Thank you

If you've followed this series, thank you. 100 entries in 4 days was an experiment in sustained output. The lesson: with a good template and clear constraints (vanilla JS, bilingual, tested, documented), the per-entry overhead is small. The hard part was deciding what to build; the building itself was mechanical.

Entry #100. Goal reached. 🎉

Top comments (0)