1. Product overview
1.1 One-sentence positioning
HYB (Hatch Your Beta) is a local-first, multi-project product launch and growth distribution console
for indie hackers and SaaS founders. It helps you manage launch materials, track 500+ acquisition channels,
and record cold-start and live-URL distribution progress —
with all data stored only in your browser's localStorage, never uploaded to a server.
1.2 What problem it solves
In the cold-start phase, the most draining work isn't writing code — it's tracking where to post, how far along each channel is, and where to resume next. HYB decomposes this into three quantifiable objects:
| Object | Entity in code | Description |
|---|---|---|
| Platform | platforms[] | Each channel has name / url / category / free / dr / custom |
| Project | projects[] | Each project has name / url / tagline / desc / collapsed — a copy-ready set of launch materials |
| Progress | progress[projectId][platformId] | Each project × channel combination records status / followUp / notes / links[] independently |
The three intersect into a "channel × project" matrix — the core data model of HYB. The "Launched" and "Featured" counts in the overview are counted by this matrix, not by channel.
1.3 Who it's for
- Indie hackers preparing a cold start on Product Hunt, Hacker News, Reddit, and AI tool directories.
- Small teams running multiple product lines, each of which needs a full launch cycle.
- Global SaaS teams that need a bilingual (EN/zh-CN) UI and records for Google, Bing, Baidu, Naver and other search engine submissions.
- Privacy-conscious users who don't want their launch plans sitting in a third-party SaaS.
2. Core features (mapped to the code)
① Multi-project launch materials
- Each project maintains four fields independently: product name, website, tagline, and description.
- "Copy all" copies in a four-block format —
[Product Name] / [Website] / [Tagline] / [Description]— to the clipboard in one click. - Individual fields can also be copied on their own (each field has a copy button).
- Project cards can be collapsed/expanded; the collapsed state is written to the
collapsedfield and persisted.
② 500+ acquisition channels
- The built-in
REAL_PLATFORMSarray covers 9 categories: AI tool directories, SaaS software reviews, launch & startup, developer tools, social & content, community promotion, business & PR, search engines, and other channels. - Includes 17 Reddit subreddits (r/SideProject, r/indiehackers, r/SaaS, r/startups, etc.) and 7 search engine webmaster platforms (Google Search Console, Bing Webmaster Tools, Yandex Webmaster, Baidu Search Resource Platform, Naver Search Advisor, Seznam, IndexNow).
- "Add channel" form: name (required), URL, category, fee (Free/Paid/Unknown), DR (0–100, optional).
- Duplicate detection on add: same name or same host triggers a confirmation dialog.
③ Domain Rating (DR) system
- The built-in
PRELOADED_DRdictionary preloads DR values for hundreds of domains (e.g., producthunt.com 91, github.com 97, linkedin.com 99). - DR is not a filter — it's a sort: ascending (low → high) or descending (high → low), with
nullalways last. - Click any DR value in the table to edit inline: Enter to save, Esc to cancel, with a toast confirmation.
④ Progress tracking (project × channel)
- Four statuses:
pending(Pending),submitted(Launched),approved(Featured),rejected(Rejected). - Follow-up date: when unfinished and earlier than today, the row shows an overdue marker on the left.
- Notes: inline input, auto-saved on blur.
- Live URLs (
links): each project × channel can hold multiple URLs, each withurl / title / date; the expandable panel supports add/edit/remove, and the date is used for goal counting.
⑤ OKR / goal system
- Per-project goals with a selectable period: this week / this month / this quarter / this year.
- The unit is "distribution points"; the calculation counts, for the current period, all live-URL entries whose
datefalls within the period. - Switching period auto-fills defaults: week=20, month=100, quarter=300, year=1000.
- Progress bar + percentage + "N days left".
- Coaching hints adapt to five conditions: no target set, 100% reached, ≤2 days left and <80%, time progress > completion +20%, completion ahead, and steady.
⑥ Filtering & search
- Search matches channel name, URL, and category label (case-insensitive, 140 ms debounce).
- Project filter: all / a specific project. When a specific project is selected, only its rows are shown.
- Category filter, status filter, fee filter (Free/Paid/Unknown).
- The status filter applies to project rows: when all project rows under a channel are filtered out, the channel is hidden.
- "Reset filters" clears everything in one click.
⑦ Import / export / reset
- Export: a JSON file with a full snapshot of
app / version / exportedAt / lang / platforms / projects / progress / okr, filenamehyb-console-backup-YYYY-MM-DD.json. - Import: supports HYB's own full format and the older flat-array (legacy) format; a confirmation dialog before import explicitly warns that current data will be overwritten.
- Reset: restores the default channel library + default project (ffee.me sample) + default OKR (month / 100).
⑧ Bilingual UI
- A complete i18n dictionary (
I18N.zh/I18N.en). Switching language re-renders page copy, filter dropdowns, the table, and the overview, and persists tohyb_v12_lang. - Category labels are also mapped bilingually (e.g., "AI工具目录" ↔ "AI Directories").
⑨ Local-first & privacy
- All persistence goes through
localStoragewith thehyb_v12_key prefix. - No login, no backend, no account system; the footer explicitly states "No login · no server upload".
- The only external requests are the Microsoft Clarity analytics script (
clarity.ms) and the logo image; business data itself never leaves the browser. - Storage write failures (e.g., quota exceeded) are caught and surfaced as "local storage is full — could not save".
3. How it compares to common alternatives
| Dimension | HYB | Spreadsheets (Excel / Notion) | Built-in platform dashboards |
|---|---|---|---|
| Channel library | ✅ 500+ preloaded channels + DR | ❌ Build it yourself | ❌ Only their own channels |
| Multiple projects | ✅ Native project × channel matrix | ⚠️ Requires manual design | ❌ Usually single product |
| Material copy | ✅ Four-block one-click copy | ❌ Manual stitching | ❌ Not available |
| Goal tracking | ✅ Auto-counted by period and live-URL date | ⚠️ Manual formulas | ❌ Not available |
| Data location | ✅ Browser local | ❌ Cloud | ❌ Platform cloud |
| Login | ✅ Not required | ❌ Required | ❌ Required |
4. FAQ
Every number and behavior below can be verified against the source code.
Q1: Do I need an account? Where is my data stored?
No account is required — there is no account system at all. All data (channels, project materials, progress, notes, live URLs, OKRs) is stored in your browser's localStorage, with keys prefixed hyb_v12_. Switching browsers, switching devices, or clearing browser data will make your data disappear. Use the Export feature to back up.
Q2: How many channels are in the channel library?
The built-in REAL_PLATFORMS array is organized into 9 categories, including Product Hunt, Hacker News, GitHub, G2, Capterra, AlternativeTo, Indie Hackers, BetaList, AppSumo, Chrome Web Store, Google Search Console, Bing Webmaster Tools, Baidu Search Resource Platform, Naver Search Advisor, and 17 Reddit subreddits. There is also a mergeCatalog mechanism: when the local channel count is ≥300 and the catalog version is below CATALOG_VERSION (currently 3), newly added channels are merged in on upgrade.
Q3: What is DR? Is the data accurate? Does it update?
DR stands for Domain Rating (0–100). HYB preloads values for hundreds of domains in PRELOADED_DR — these are static preset values and do not update automatically. DR is not a filter but a sort (ascending/descending, with null always last). You can click any DR value in the table to edit it manually; changes are persisted.
Q4: Why does a channel disappear when I filter by "Featured"?
Because the status filter applies to project rows: when all project rows under a channel are filtered out, the entire channel is hidden. To see only one project's featured status, set the Project filter to that specific project first, then choose the status.
Q5: How are "distribution points" in the OKR calculated?
The calculation counts, for the current project and current period, all entries in each channel's links array whose date falls within the period's start and end dates. Only live URLs with a date count toward the goal. Newly added live URLs default to today's date. Switching period (week/month/quarter/year) auto-fills default targets of 20/100/300/1000, which you can change manually.
Q6: What does the left-side bar / red follow-up date mean?
When a project × channel follow-up date is earlier than today and the status is neither "Featured" nor "Rejected", the row is marked overdue (row-overdue) with a 2px black inset on the left. It reminds you that follow-up is due but not yet closed.
Q7: Can I manage multiple products at once?
Yes. Each project has its own four-field materials, its own progress matrix, and its own OKR. The overview's per-project breakdown lists each project's live URLs, launched count, and featured count separately. Deleting a project also deletes its progress, notes, live URLs, and OKR (with a confirmation dialog).
Q8: Does import overwrite my existing data?
Yes. A confirmation dialog appears before import, explicitly stating that importing will overwrite all current channels, project materials, distribution progress, and goal settings. Import supports two formats: the full JSON exported by HYB (with platforms/projects/progress/okr), and the older flat array format (migrated as legacy into the first project).
Q9: What should I do when adding a channel triggers a "same name or host already exists" warning?
This is duplicate protection. It is based on platformUrlKey (host, with path preserved for some hosts) and platformNameKey (normalized name comparison, including alias mappings such as twitter→x and blogspotcom→bloggercom). If you really want to add it, click "Confirm" in the dialog to continue; it will be added as a custom channel (custom: true).
Q10: Is my data uploaded to a server?
Business data is not uploaded. The only external scripts on the page are Microsoft Clarity (clarity.ms) for visit analytics and the hyb.vip/logo.svg image. Your channels, projects, progress, notes, and live URLs are written only to localStorage. If a write fails (e.g., private mode or full storage), you'll see a "local storage is full — could not save" message.
Q11: Which browsers are supported?
The code uses modern APIs including localStorage, navigator.clipboard (falling back to execCommand('copy') in non-secure contexts), Intl.DateTimeFormat, URL, and Array.from(...normalize('NFKC')). The structured data declares browserRequirements: Requires JavaScript. Data is stored in localStorage. and operatingSystem: Any. Recent versions of Chrome, Edge, Firefox, and Safari are recommended.
Q12: Can I switch the UI language? Does it affect my data?
Yes — the language switcher in the top-right supports English and Simplified Chinese. Switching only changes UI copy (including bilingual category labels) and does not affect data. The language choice is persisted in hyb_v12_lang. The default language is English (the code sets lang = storedLang === 'zh' ? 'zh' : 'en').
Q13: How do I back up, and how often should I?
Click "Export" in the top-right to download hyb-console-backup-YYYY-MM-DD.json, containing a full snapshot of platforms, projects, progress, and okr. It is recommended to export after every significant change (bulk channel additions, bulk status updates) and keep multiple dated versions. To restore, import the same file.
Q14: Why did my data disappear after clearing browser cache?
Because data lives in localStorage — clearing cache/site data clears it too. That is the cost of local-first, and also the cost of privacy. Make a habit of exporting backups.
Q15: Is HYB free?
The structured data declares isAccessibleForFree: true with offers of price: 0, priceCurrency: USD. The current version (v12.0) is free to use with no login and no paywall.
5. How it works
Told in data-flow order. All function names and key names are from the source code.
5.1 Data model (three layers)
projects[] each project: id, name, url, tagline, desc, collapsed
platforms[] each channel: id, name, url, category, free, dr, custom
progress[projectId][platId] each combination: status, followUp, notes, links[{url,title,date}]
okrMap[projectId] each project: cycle(week|month|quarter|year), target
Relationship: progress is a sparse matrix of projects × platforms; only combinations that have been operated on have entries.
On read, getProgress(pid, platId) auto-creates an empty record if missing (emptyProgress() returns status: 'pending').
5.2 Boot flow (init())
loadState(): decides between migration and direct read based on the version number.- If
hyb_v12_version< 12 or there is no v12 platforms, it first triesmigrateFromV10()(migrating fromffee_console_v10_*). - Reads
hyb_v12_platforms; if missing, reads the legacyffee_promo_data_final_v2; if still missing, usesdefaultPlatforms()(i.e.,REAL_PLATFORMS+PRELOADED_DR). - When the catalog version is below
CATALOG_VERSION(3) and the channel count is ≥300,mergeCatalog()merges new channels and recordscatalogAdded. - Reads projects / progress / okr / lang / ui / savedAt, cleans each via
normalize*, and ensures unique ids viadedupeIds.
- If
bindEvents(): wires up all buttons, filters, table event delegation, keyboard Esc, page visibility, andpagehide/beforeunloadsaves.applyI18n()+startClock()(refreshes the top date/time every second).buildFilterOptions()→renderProjects()→updateMaterialsUi()→renderTable()→renderOverview().- If
needsCatalogPersistorneedsCopyRefreshis set, one extra persistence pass is made.
5.3 Render flow
- Overview:
renderStats()iterates overplatforms × projectsto accumulate live-URL count, launched count, and featured count, plus a per-project breakdown;renderGoal()computes the current project's OKR within the period, the percentage, days left, and the coaching message. - Materials:
renderProjects()builds project cards from template strings;autoGrowAllTextareas()makes textareas auto-size. - Channel table:
filteredPlatforms()first filters channels by search/category/fee, then usesrowsFor(p)to check whether the channel has any visible rows under the current project/status; sorting only handles DR (null always last).renderTable()outputs the main row + sub-rows (when multiple projects) + the expanded live-URL panel.
5.4 Interaction & saving
- Material field input: the
inputevent updates the in-memory object;queueProjectSave()debounces 260 ms before writing to localStorage;flushProjectSave()forces a flush on export, language switch, page hide, and unload. - Table status/date/notes: the
changeevent callspersistProgress()immediately; status and date changes re-render the table and overview. - Live URL add/edit/remove: updates
links[index][field]and persists immediately; a date change triggersrenderGoal()to recompute the goal. - DR inline editing: replaces the button with an
input.dr-input; Enter/blur saves, Esc cancels;normDr()validates 0–100 and rounds, treating invalid values as null. - All writes go through
writeJSON(), which catchesQuotaExceededErrorand similar exceptions and shows a toast.
5.5 Import / export / reset
- Export:
exportData()assembles{app, version, exportedAt, lang, platforms, projects, progress, okr}and downloads it as a Blob. - Import:
importFile()parses the JSON and determines whether it's a flat array (legacy) or a full object; a full object reads platforms/projects/progress/okr, while legacy migrates old fields into the first project; after confirmation it replaces the in-memory state, persists each piece, and re-renders. - Reset:
resetAllData()restoresdefaultPlatforms()+ the default project + the default OKR, clears filters and expanded state, and persists each piece.
5.6 Migration & compatibility
- v10 → v12: reads
ffee_console_v10_*and migrates old platforms' status/followUp/notes/links to the "target project" (first the current project, then the project named ffee.me, thenproj_ffee, then the first project). - Legacy materials:
migrateLegacyProjects()readsffee_matsand converts zhName/zhUrl/zhTagline/zhDesc into a single project. - Channel dedup:
dedupePlatforms()merges byplatformUrlKeyandplatformNameKey, keeps non-empty DR/free, and unionscustom.
5.7 Privacy & boundaries
- The persistence layer is
localStorageonly; there is no fetch/XHR uploading of business data. - Clipboard prefers
navigator.clipboard(requires a secure context), falls back toexecCommand('copy'), and on further failure prompts manual copy. - The only external dependencies are the Clarity script and the logo image;
noscriptexplicitly states that JavaScript is required.
6. Step-by-step tutorial
6.1 First launch
- Open the HYB console in your browser. The current date, weekday, and time are displayed at the top (refreshed every second).
- Switch between English / Simplified Chinese in the top-right. Switching only changes UI copy, not data.
- The page has four blocks: Overview (stats + goal), Launch Materials, Growth Channels, and the footer (reset entry).
6.2 Create your first project
- In the Launch Materials section, click Add project. An empty card is generated, scrolled into view, and the cursor lands in "Product name".
- Fill in the four fields:
- Product name: e.g.,
ffee.me - Website: e.g.,
https://ffee.me/(the card subtitle shows the host in real time) - Tagline: one sentence that says what it is
- Description: selling points, target users, differentiation
- Product name: e.g.,
- Each field has a Copy button for copying that field alone.
- Copy all in the card's top-right copies in a four-block format in one click:
[Product Name] … [Website] … [Tagline] … [Description] …
- Click the chevron on the right of the card to collapse/expand; the state is persisted.
- To manage multiple products, repeat "Add project". Each project has independent materials, progress, and OKR.
- To delete a project, click the trash icon; the confirmation dialog warns that "materials, progress, notes, live URLs, and goals will all be removed".
6.3 Pick channels and record progress
- Scroll to "Growth Channels". Columns: Category / Channel / DR / Project / Status / Follow-up / Live URLs / Notes.
- Filters (the row above the table header):
- Search box: type a channel name, URL, or category keyword (140 ms debounce).
- Project: All projects / a specific project. When a specific project is selected, only its rows are shown.
- Category, status, fee: combine as needed.
- DR sort: default / ascending / descending (null always last).
- Reset filters: clears all conditions in one click.
- Change status: use the inline dropdown to pick Pending / Launched / Featured / Rejected. Saved immediately.
- Set a follow-up date: the inline date field. If the date is earlier than today and the status is neither Featured nor Rejected, a black bar appears on the left of the row to mark overdue.
- Write notes: the inline notes field auto-saves on blur; its placeholder is "Click to add…".
- Record live URLs:
- Click the button in the "Live URLs" column to expand the panel; the number on the button is the current live-URL count.
- Click Add live URL to add a row: URL, title/note, date (defaults to today).
- The date matters — OKRs count by live-URL date.
- Each row has a trash icon to delete it.
- Multiple projects on the same channel: when a channel has multiple projects attached, the table shows a main row + sub-rows, each with independent status/date/notes/live URLs.
6.4 Set a goal and watch progress
- In the Overview's "Goal" row:
- Choose a period: this week / this month / this quarter / this year (switching auto-fills the default target: 20/100/300/1000).
- Enter "Achieve __ distribution points".
- The right side shows the percentage, progress bar, "X of Y done", and "N days left".
- Calculation rule: only counts live-URL entries under the current project whose date falls within the current period. Live URLs without a date are not counted.
- Coaching hints change based on the situation:
- Target is 0 → prompt to set a target
- 100% reached → congratulations
- ≤2 days left and <80% → warning to sprint
- Time progress > completion +20% → behind schedule
- Completion > time progress → ahead of schedule
- Otherwise → steady
6.5 Add a custom channel
- Click Add channel in the top-right of the channel table.
- Fill in: channel name (required), URL, category, fee (Free/Paid/Unknown), DR (0–100, optional).
- If the same name or host already exists, a confirmation dialog appears; confirming adds it as a custom channel (shown with a small "Custom" tag in the table).
- Custom channels participate in filtering, sorting, and progress tracking just like preloaded ones.
6.6 Edit DR
- In the DR column, click any DR value (showing
DR —means empty). - It turns into an input; enter 0–100, Enter to save, Esc to cancel.
- Invalid values (non-numeric, out of range) are cleared to null.
- A toast confirms "DR saved: xx" or "DR cleared".
6.7 Export backup & import to restore
Export
- Click Export in the top-right.
- Downloads
hyb-console-backup-YYYY-MM-DD.json, containing full platforms/projects/progress/okr data. - Export after every bulk change and keep multiple copies.
Import
- Click Import in the top-right and select a JSON file.
- A confirmation dialog appears: "Importing will overwrite all current channels, project materials, distribution progress, and goal settings".
- Confirm to replace everything and re-render; a toast shows "Imported: X channels · Y projects".
- Supports the older flat-array format (migrated into the first project).
6.8 Reset data
- Click Reset data in the footer.
- The confirmation dialog warns that "all channel distribution progress, notes, live records, project materials, and goals will be restored to their initial state".
- Confirming restores the default channel library + default project + default OKR (month/100).
6.9 Suggested daily rhythm
- Before launch: complete the four-field materials → export a backup → filter by category to pick target channels and set their status to "Pending".
- During launch: after each post, change the status to "Launched", set a follow-up date, and record the live URL with a date.
- After launch: check overdue rows (left-side bar) daily; review the OKR progress and coaching hint weekly; switch to "Featured" once a channel features you.
- Weekly: export a JSON with the date in the filename and archive it.
7. Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Data disappeared after refresh | Cleared site data / switched devices | Import the exported JSON |
| "Local storage is full" message | localStorage full or private mode | Export a backup, clear site data, use a normal window |
| Copy does nothing | Non-secure context and execCommand failed | Select and copy manually, or access via https |
| Channel disappears after choosing a status | Status filter applies to project rows | Pick a specific project first, then the status |
| OKR doesn't increase | Live URL has no date or date is outside the period | Add a date to the live URL |
| DR sort doesn't change | Null DR always sorts last | Click the DR value to edit it manually |