{"title":"Data Cleanup","description":"\u003cp\u003eClean contact lists, compare price lists, fix addresses — privately, on your own machine.\u003c\/p\u003e","products":[{"product_id":"address-phone-formatter","title":"Address - Phone Formatter","description":"\u003cp\u003ePaste or upload a CSV of contacts, tell it which columns are phone numbers and which are addresses (it guesses first, you confirm), and get back a consistently formatted copy — country-code-aware phone formatting for US\/Canada, and address *normalization* (casing, whitespace, abbreviation style) for the rest. Anything it can't confidently parse gets flagged for a quick manual check instead of being silently mangled or dropped. One-time purchase, works fully offline, never touches a server.\u003cbr\u003e\u003c\/p\u003e","brand":"My Store","offers":[{"title":"Default Title","offer_id":54669062406481,"sku":null,"price":5.0,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/1073\/4226\/6705\/files\/01-start.png?v=1787566705"},{"product_id":"contact-list-cleaner","title":"Contact List Cleaner","description":"\u003cp class=\"unt-pitch\"\u003e\u003cem\u003eFind duplicate contacts in a messy CSV — exact matches on email\/phone, plus real fuzzy matching for typos and nicknames Excel's dedupe can't catch — and review every merge before anything changes.\u003c\/em\u003e\u003c\/p\u003e\u003cp\u003eA tool to remove duplicate contacts from a messy CSV, two ways: an exact-match pass on normalized email\/phone (certain duplicates), and a fuzzy pass on names using real string-similarity (Levenshtein edit-distance plus token matching) that catches what Excel's built-in \"Remove Duplicates\" can't — nicknames, typos, and reordered names. Nothing is ever merged automatically: every group is presented for review, you pick which record to keep, and genuinely different people who happen to have similar names stay separate unless you say otherwise. One-time purchase, works fully offline, never uploads your contact list anywhere.\u003c\/p\u003e\u003ch3\u003eWhat it does\u003c\/h3\u003e\u003cp\u003e\u003cstrong\u003eWhat it does:\u003c\/strong\u003e Reads a CSV (pasted or uploaded), guesses which columns are Name (or separate First\/Last), Email, and Phone from the header first and the actual values second, then lets you confirm the mapping before anything is processed. An exact-match pass groups records that share an identical normalized email or phone number — about as certain as duplicate detection gets. A separate fuzzy pass compares names with a real algorithm: Levenshtein edit-distance on the full name plus a token-based best-match comparison (so reordered names like \"Smith, Priya\" and \"Priya Smith\" score identically), at a sensitivity you control with a slider. Matched groups use complete-link (clique) clustering rather than naive chaining, specifically to stop one weak connecting match from pulling an unrelated third person into a group — verified in testing against exactly that failure mode before shipping. Every group is shown for review with a similarity score for each record against your chosen keeper: certain (exact-match) groups default their extra records to \"remove as duplicate\" checked, since that's as sure as the tool gets; fuzzy-only groups default to \u003cstrong\u003eunchecked\u003c\/strong\u003e, on purpose, because a nickname\/typo match and two different people with similar names look identical to any algorithm — only a human reviewing the actual records can tell those apart. Export produces a clean CSV with casing and whitespace normalized on every kept record, plus a \u003ccode\u003ededupe_notes\u003c\/code\u003e column documenting exactly what was merged or flagged.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eWho it's for:\u003c\/strong\u003e A small business or solo operator merging contact lists collected over time — sign-up sheets from multiple events, a CRM export combined with a spreadsheet someone kept by hand, a mailing list assembled from several sources — where the same person shows up more than once under slightly different spellings, casing, or contact details.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eExample scenario:\u003c\/strong\u003e A workshop business has two years of in-person sign-up sheets, now typed into one spreadsheet, plus a newer online form export. The same repeat customer appears as \"Jon Smith\" on one sheet and \"Jonathan Smith\" with a different email on another; a data-entry typo turned \"Amy Turner\" into \"Amy Turnier\" on a third. Meanwhile the list also has a genuine \"John Smith\" and a genuine \"John Smyth\" who are two different, real people who've never met. Running the merged list through this tool catches the Jon\/Jonathan and Amy\/Amy Turnier pairs as flagged, reviewable groups (nothing merged until confirmed) while never silently combining John Smith and John Smyth into one contact — both stay separate by default, exactly because the tool can't (and doesn't pretend to) know they're different people on its own.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eWhat it explicitly does not do:\u003c\/strong\u003e\u003c\/p\u003e\u003cul\u003e\n\u003cli\u003eDoes not merge anything automatically. Every duplicate group — certain or fuzzy — is a review screen, not a decision. Certain (exact-match) groups pre-check their extra records for removal since that's as confident as the tool gets, but you can uncheck any of them before exporting.\u003c\/li\u003e\n\u003cli\u003eDoes not guarantee it catches every real duplicate, or that every flagged group is actually the same person. Fuzzy name matching is a similarity score, not identity verification — it will occasionally flag two genuinely different people with close names (that's *why* review exists), and at a given sensitivity it can also miss a real nickname pair that scores just under the threshold (the sensitivity slider exists for exactly this trade-off).\u003c\/li\u003e\n\u003cli\u003eDoes not do full postal-address validation or phone-number reformatting — this tool's job is deduplication. Email and phone are only whitespace\/casing-normalized on export, not reformatted; for full country-code-aware phone formatting, the companion \u003cstrong\u003eAddress \/ Phone Formatter\u003c\/strong\u003e tool in this same line does that job.\u003c\/li\u003e\n\u003cli\u003eDoes not currently split one review group into two independent merge decisions when a low sensitivity setting happens to cluster two unrelated duplicate pairs together (rare, but possible) — you can still keep both pairs correctly separated by hand within that one group, or re-export and re-run at a higher sensitivity for a cleaner split.\u003c\/li\u003e\n\u003cli\u003eDoes not remember anything between sessions — reload the page and the loaded CSV, mapping, and review decisions are gone.\u003c\/li\u003e\n\u003c\/ul\u003e\u003ch3\u003eFeatures\u003c\/h3\u003e\u003cul\u003e\n\u003cli\u003eTwo-pass duplicate detection: an exact-match pass on normalized email\/phone (certain duplicates), plus a fuzzy pass on names using real Levenshtein edit-distance and token-based similarity — catches \"Jon Smith\"\/\"Jonathan Smith\", typos, and reordered names Excel's exact-match dedupe misses entirely\u003c\/li\u003e\n\u003cli\u003eComplete-link (clique) clustering, not naive chaining — a weak indirect match can't silently pull an unrelated third person into a duplicate group (a real failure mode this tool was specifically tested against and fixed)\u003c\/li\u003e\n\u003cli\u003eAdjustable fuzzy-match sensitivity slider (50–95%) — lower catches more nicknames\/typos, higher keeps groups tighter, re-run any time\u003c\/li\u003e\n\u003cli\u003eNothing is ever auto-merged: every group is a review screen with a similarity score per record against your chosen keeper; fuzzy-only groups default every removal checkbox to unchecked so two different people with similar names are never silently combined\u003c\/li\u003e\n\u003cli\u003eCasing and whitespace normalized on export, with a \u003ccode\u003ededupe_notes\u003c\/code\u003e column documenting exactly what was merged or flagged — nothing silent\u003c\/li\u003e\n\u003cli\u003eRuns entirely offline — no network requests, no analytics, nothing uploaded, verifiable by reading the page's own source; tested at 6,000 rows in a real browser (under 3 seconds, chunked so the tab stays responsive rather than freezing)\u003c\/li\u003e\n\u003c\/ul\u003e\u003ch3\u003eBrowser support\u003c\/h3\u003e\u003ctable\u003e\n\u003cthead\u003e\u003ctr\u003e\n\u003cth\u003ePlatform\u003c\/th\u003e\n\u003cth\u003eResult\u003c\/th\u003e\n\u003cth\u003eNotes\u003c\/th\u003e\n\u003c\/tr\u003e\u003c\/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003eDesktop Chrome (macOS)\u003c\/td\u003e\n\u003ctd\u003e**Full**\u003c\/td\u003e\n\u003ctd\u003eOpened and visually confirmed in the real, installed Chrome app. The complete functional flow — CSV load, column mapping, exact + fuzzy duplicate detection, interactive keeper\/removal review, CSV export (a real file download, verified by opening the downloaded file) — was additionally run end-to-end through Chromium's real engine via Playwright, including a 6,000-row performance run and edge-case tests (wrong file type, malformed CSV, zero-duplicate result, blank fields).\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eDesktop Edge \/ Brave\u003c\/td\u003e\n\u003ctd\u003eNot separately tested\u003c\/td\u003e\n\u003ctd\u003eBoth are Chromium-based like Chrome, where the tool was confirmed Full; neither is installed on this machine, so this is an inference from the shared engine, not an independent test.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eDesktop Safari (macOS)\u003c\/td\u003e\n\u003ctd\u003e**Full**\u003c\/td\u003e\n\u003ctd\u003eOpened and visually confirmed in the real, installed Safari app. No Safari-specific capability gap exists (no File System Access API, nothing Chromium-only), so the full functional flow — including the exact same 25-row hand-verified duplicate detection and CSV export — was run end-to-end through Safari's real WebKit engine via Playwright, with byte-identical export output to Chrome.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eDesktop Firefox (macOS)\u003c\/td\u003e\n\u003ctd\u003e**Full**\u003c\/td\u003e\n\u003ctd\u003eOpened and visually confirmed in the real, installed Firefox app. Full functional flow run end-to-end through Firefox's real Gecko engine via Playwright, with byte-identical export output to Chrome and Safari.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eSmall viewport \/ touch\u003c\/td\u003e\n\u003ctd\u003e**Checked**\u003c\/td\u003e\n\u003ctd\u003eNot a physical phone, but genuinely emulated (not assumed): a 390×844px touch-enabled mobile viewport, with the full load → map → review → export flow actually run through it. Confirmed no horizontal overflow (`document.body.scrollWidth` matched the viewport width exactly).\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eiOS Safari (iPhone\/iPad)\u003c\/td\u003e\n\u003ctd\u003e**Not verified**\u003c\/td\u003e\n\u003ctd\u003eNo physical iOS device available. iOS Safari shares desktop Safari's WebKit engine and this tool has no capability gap to begin with, so the same behavior is expected — but this is an inference, not a test on real hardware, and is not claimed as verified.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eAndroid Chrome\u003c\/td\u003e\n\u003ctd\u003e**Full**\u003c\/td\u003e\n\u003ctd\u003eTested on a real Android device in Chrome — works.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003c\/tbody\u003e\n\u003c\/table\u003e\n\u003cp\u003e\u003cem\u003e**Method note on \"automated testing\":** beyond opening each real installed browser app and visually confirming it loads cleanly, the functional flow (CSV parsing, column auto-detection, exact + fuzzy duplicate detection, interactive review UI, CSV export) was exercised end-to-end using Playwright driving each engine's own real Chromium\/WebKit\/Gecko binary — the same unmodified code, actually run, not read from source and assumed. A 25-row hand-built messy test CSV — with exact duplicates, nickname\/typo fuzzy matches, a deliberately adversarial three-way \"chaining\" test case, and two genuinely-distinct similar-named people who must never be auto-merged — was hand-calculated for expected groupings *before* the matching algorithm was wired into the UI, using a standalone Node test harness (24 assertions, all passing) run against the exact same matching code later embedded in the page. The three real browser engines then independently reproduced byte-identical exported CSVs from that fixture.\u003c\/em\u003e\u003c\/p\u003e\u003ch3\u003eFAQ\u003c\/h3\u003e\u003cp\u003e\u003cstrong\u003eDoes my contact list get uploaded anywhere?\u003c\/strong\u003e\u003cbr\u003eNo. The entire tool is one static HTML file with no server component and no network calls. Every row is parsed and matched locally in your browser tab. You can open it with your Wi-Fi off, or read the page's source yourself to confirm there's nothing that sends data anywhere. (Worth knowing: research for this tool found that several free online CSV-duplicate-removal tools genuinely do process client-side too — but plenty of \"smart\"\/CRM-style dedupe tools require uploading your list to their servers or connecting a live CRM account, which is a real concern for a customer or client contact list.)\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eWill this ever merge two different real people by mistake?\u003c\/strong\u003e\u003cbr\u003eIt will never merge anyone *automatically* — that's the core safeguard. The fuzzy pass will sometimes flag two genuinely different people as a \"possible match\" if their names happen to be close (a one-letter typo in a last name scores just as high as, sometimes higher than, a real nickname variant — that's an inherent property of edit-distance matching, not a bug this tool can fully outsmart). Because of that, every fuzzy-only group defaults its \"remove as duplicate\" checkboxes to \u003cstrong\u003eunchecked\u003c\/strong\u003e, and shows you each record's similarity score against your chosen keeper so you can judge for yourself. Nothing leaves the review screen and enters your exported file unless you explicitly check it for removal.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eHow is this different from Excel's or Google Sheets' built-in \"Remove Duplicates\"?\u003c\/strong\u003e\u003cbr\u003eExcel and Sheets' native dedupe only catches *exact* matches — same research for this tool confirmed it plainly won't recognize \"Jon Smith\" and \"Jonathan Smith,\" or \"ABC Company LTD\" and \"Company ABC LTD,\" as the same thing. This tool does that exact-match pass too (on email\/phone), but adds a genuine fuzzy-matching layer on names — real edit-distance and token-based similarity, not a formula you have to build yourself — which is the actual gap between free spreadsheet tools and this one. (Power Query and paid Excel add-ins like Ablebits' Fuzzy Duplicate Finder can do fuzzy matching too, but the add-in route means buying a $99+ suite and working inside Excel; this is a $8 single-purpose file that works anywhere.)\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eDoes this work on my iPhone\/iPad?\u003c\/strong\u003e\u003cbr\u003eAlmost certainly, but this is stated honestly rather than guessed: iOS Safari shares the same WebKit engine as desktop Safari, where the full duplicate-detection and export flow was verified end-to-end, and this tool has no feature that's specific to a desktop browser (no drag-and-drop file system access, nothing Chromium-only). That said, it hasn't been tested on an actual iPhone or iPad, so it's listed as \"not verified\" rather than \"confirmed\" on the platform-support table above — genuinely likely to work, not confirmed to.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eCan I use this for client or commercial work?\u003c\/strong\u003e\u003cbr\u003eYes — personal and commercial use are both fine.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eIs there a subscription?\u003c\/strong\u003e\u003cbr\u003eNo. One-time purchase, yours to keep and reuse forever.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eWhat happens to my file if I lose the download?\u003c\/strong\u003e\u003cbr\u003eThe tool itself is a single HTML file you keep — redownload it from your purchase confirmation if needed. It doesn't store or remember any contact list you've ever loaded into it; that only ever lives in your browser tab while it's open.\u003c\/p\u003e","brand":"My Store","offers":[{"title":"Default Title","offer_id":54670164722001,"sku":null,"price":9.0,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/1073\/4226\/6705\/files\/01-hero_3cf39b37-b187-434b-bea8-a7f1de7667af.png?v=1787610359"},{"product_id":"ocr-scanner","title":"OCR Scanner - Photo to Text Converter","description":"\u003cp class=\"unt-pitch\"\u003e\u003cem\u003eA photo to text converter that runs entirely in your browser — batch-scan a whole folder of photos into real, copyable text, with nothing ever uploaded.\u003c\/em\u003e\u003c\/p\u003e\u003cp\u003eDrop in one or more photos of printed pages and get back real, selectable text you can copy or download — no typing, no account, no upload. This image to text converter runs entirely offline in your browser using a bundled WebAssembly OCR engine, and unlike most free online converters, scans a whole batch of photos at once.\u003c\/p\u003e\u003ch3\u003eWhat it does\u003c\/h3\u003e\u003cp\u003eOCR Scanner reads printed text out of a photo or scan and turns it into plain text you can copy, edit, or save — a stack of paper notes, a printed contract page, a whiteboard photo, a scanned form. Drop in one image or twenty at once; each is scanned in place with a live progress bar, and you get a \"Copy text\" and \"Download .txt\" button per image, plus a \"download all as one file\" option for a whole batch.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eWho it's for:\u003c\/strong\u003e a freelancer or small-business owner who has photos of printed documents and wants the text out of them without retyping — especially when the document has anything sensitive in it, since this tool never sends the image anywhere.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eExample scenario:\u003c\/strong\u003e a freelance photographer photographs four pages of printed shoot notes on their phone during a client meeting. Back at their desk, they drop all four photos into OCR Scanner, and two minutes later have plain text they can paste straight into an invoice or a follow-up email — instead of retyping four pages by hand.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eWhat this does not do:\u003c\/strong\u003e it does not extract structured fields (no automatic \"vendor \/ date \/ total\" parsing — that's a separate, purpose-built tool). It does not pre-process images (no auto-crop, no deskew, no contrast correction) — whatever's in the photo is what gets scanned. It only reads English text in this version. It reads printed text; handwriting recognition is not reliable with this engine and isn't claimed here.\u003c\/p\u003e\u003ch3\u003eFeatures\u003c\/h3\u003e\u003cul\u003e\n\u003cli\u003eScans one or many photos in a single batch, each with its own live progress bar.\u003c\/li\u003e\n\u003cli\u003eRuns entirely offline after the file loads — zero network requests during scanning, verified directly, not just claimed.\u003c\/li\u003e\n\u003cli\u003eCopy any result to your clipboard, or download it as a \u003ccode\u003e.txt\u003c\/code\u003e file — individually or all at once.\u003c\/li\u003e\n\u003cli\u003eShows Tesseract's own real confidence score per image, so you know when to double-check a result.\u003c\/li\u003e\n\u003cli\u003eClear error messages for non-image files, corrupted images, and images with no readable text — nothing silently fails.\u003c\/li\u003e\n\u003cli\u003eWorks in both light and dark mode automatically, matching your system setting.\u003c\/li\u003e\n\u003c\/ul\u003e\u003ch3\u003eBrowser support\u003c\/h3\u003e\u003ctable\u003e\n\u003cthead\u003e\u003ctr\u003e\n\u003cth\u003ePlatform\u003c\/th\u003e\n\u003cth\u003eResult\u003c\/th\u003e\n\u003cth\u003eNotes\u003c\/th\u003e\n\u003c\/tr\u003e\u003c\/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003eDesktop Chrome\u003c\/td\u003e\n\u003ctd\u003e**Full**\u003c\/td\u003e\n\u003ctd\u003eVerified via the real installed Chrome.app (visual, screenshot) and via Playwright's Chromium engine (functional): correct OCR output, zero network requests, zero console errors on normal runs. A realistic 4200×5440px (22.8MP) document photo OCR'd in 2.8s; a smaller 1700×2200px document in 2.1s.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eDesktop Safari\u003c\/td\u003e\n\u003ctd\u003e**Full**\u003c\/td\u003e\n\u003ctd\u003eVerified via the real installed Safari.app (visual, screenshot) and via Playwright's WebKit engine (functional, same rendering engine as Safari): correct OCR output, zero network requests, zero console errors. 1700×2200px document OCR'd in 1.8s.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eDesktop Firefox\u003c\/td\u003e\n\u003ctd\u003e**Full, but measurably slower**\u003c\/td\u003e\n\u003ctd\u003eVerified via the real installed Firefox.app (visual, screenshot) and via Playwright's Firefox engine (functional): output is fully correct and zero network requests\/console errors, but WASM execution is real and consistently slower than Chromium\/WebKit for this workload — a small 900×400 test image took ~2.9s in Firefox vs. ~0.2s in Chromium\/WebKit, and the 1700×2200px document took ~29s vs. ~2s. This is disclosed honestly in the platform-support copy below rather than smoothed over.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eiOS Safari\u003c\/td\u003e\n\u003ctd\u003e**Not tested**\u003c\/td\u003e\n\u003ctd\u003eNo iOS device available on this machine. Explicitly left unchecked rather than assumed.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eAndroid Chrome\u003c\/td\u003e\n\u003ctd\u003e**Full**\u003c\/td\u003e\n\u003ctd\u003eTested on a real Android device in Chrome — works.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eSmall viewport \/ touch\u003c\/td\u003e\n\u003ctd\u003e**Full (emulated)**\u003c\/td\u003e\n\u003ctd\u003eVerified via Chromium's iPhone 13 device emulation (390×844, touch-enabled): layout stays single-column and readable, OCR runs correctly, zero console errors. Explicitly a desktop emulation, not a real device — noted as such, not claimed as real mobile testing.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003c\/tbody\u003e\n\u003c\/table\u003e\u003ch3\u003eFAQ\u003c\/h3\u003e\u003cp\u003e\u003cstrong\u003eDoes my photo get uploaded anywhere?\u003c\/strong\u003e No. Every image is decoded and scanned entirely inside your browser tab. This was verified directly with network-request interception during testing — zero requests leave the page during OCR, on any image.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eWhy is the file so large (7.66 MB)?\u003c\/strong\u003e The entire OCR engine — a WebAssembly build of the Tesseract OCR engine, plus the English language training data it needs to recognize text — is built directly into this one HTML file, so it works completely offline with no separate install or download. That's genuinely a few megabytes of engine and language data, not bloat.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eHow accurate is this, really?\u003c\/strong\u003e On a clean, well-lit, right-side-up printed page, accuracy was very high in testing (95%+ confidence, effectively perfect transcription on a full test document). On a photo rotated a few degrees, or blurry, accuracy drops — real testing showed individual words getting misread (e.g. \"brown\" → \"prown\") and confidence scores as low as the mid-50s% on a deliberately rough, blurry-and-rotated test photo. Always read the result before relying on it, especially for numbers.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eCan I use this for client or commercial work?\u003c\/strong\u003e Yes. One-time purchase, personal and commercial use included.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eIs there a subscription?\u003c\/strong\u003e No. One-time purchase, yours to keep and reuse.\u003c\/p\u003e","brand":"My Store","offers":[{"title":"Default Title","offer_id":54670816510289,"sku":null,"price":9.0,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/1073\/4226\/6705\/files\/01-hero_2a82b7ab-8f76-44a2-ada6-a11900a89eb7.png?v=1787610477"},{"product_id":"price-list-comparator","title":"Price List Comparator","description":"\u003cp class=\"unt-pitch\"\u003e\u003cem\u003eDrop in your old and new price list, and see exactly what changed, what's new, and what's gone — cost, price, and quantity deltas computed for you, right in your browser, nothing uploaded.\u003c\/em\u003e\u003c\/p\u003e\u003cp\u003eCompare two price lists — last month's and this month's, or any \"before\" and \"after\" CSV — by picking the column that uniquely identifies each row (SKU, item code, or product name), and get back a clear comparison: every item that changed (with the old and new value, the dollar delta, and the percent change), every item that's new, and every item that disappeared. Works across multiple value columns at once — cost, price, and quantity in the same pass — and exports the full diff as a CSV. One-time purchase, works fully offline, never touches a server.\u003c\/p\u003e\u003ch3\u003eWhat it does\u003c\/h3\u003e\u003cp\u003e\u003cstrong\u003eWhat it does:\u003c\/strong\u003e Reads two CSVs (pasted or uploaded), lets you confirm which column is the unique key to match rows on — auto-detected first from column names like SKU\/ID\/code, with a plain-language warning if nothing looked confident enough to auto-detect — then confirms which other columns to compare (cost, price, quantity, or anything else). Rows are matched between the two files by an \u003cstrong\u003eexact, case-insensitive\u003c\/strong\u003e key comparison — deliberately not fuzzy matching, so a typo in a SKU shows up honestly as one row removed and one row added rather than a silently wrong match. For every matched row, each compared column is checked: if both the old and new values are numeric, the tool computes the dollar delta and percent change automatically; if either value is blank, that's flagged with a plain-language note instead of guessed at. Rows that exist in only one file are marked added or removed. The results are filterable by status (changed\/added\/removed\/unchanged), sortable by key, status, or largest percent change, and exportable as a single diff CSV with every before\/after\/delta\/%-change value plus notes — ready to hand to a bookkeeper or drop into a repricing spreadsheet.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eWho it's for:\u003c\/strong\u003e A shop owner, small distributor, or freelancer who gets a new supplier price sheet or inventory export periodically and needs to know precisely what moved before repricing shelves, updating a catalog, or flagging a supplier increase.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eExample scenario:\u003c\/strong\u003e A shop owner receives this month's supplier price list as a CSV. Twenty items are unchanged, nine had a cost or price change, two were discontinued, and three new items were added — including one where the supplier's product code got fat-fingered between months (a genuine typo, not a real SKU change). The old free options are a bookkeeper manually eyeballing two spreadsheets, or building a VLOOKUP formula by hand for a one-off comparison. Instead, they load last month's and this month's CSV, confirm the SKU column, and immediately see: 9 changed (each with the exact $ and % move), 2 removed, 3 added (the typo'd row correctly shows up as one removed + one added rather than being silently treated as a real price change) — then export the diff CSV to hand to whoever updates the shelf tags.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eWhat it explicitly does not do:\u003c\/strong\u003e\u003c\/p\u003e\u003cul\u003e\n\u003cli\u003eDoes not fuzzy-match keys. A SKU that's misspelled, mistyped, or reformatted between the two files (a letter swapped for a similar-looking digit, extra whitespace beyond simple trimming, a reordered code) will \u003cstrong\u003enot\u003c\/strong\u003e be matched — it will show up as one row removed and one row added. This is a deliberate design choice (silently \"fixing\" a mismatched key is exactly the kind of guess this tool won't make), not a bug, but it means very messy source data needs a cleanup pass first for a truly clean diff.\u003c\/li\u003e\n\u003cli\u003eDoes not merge or dedupe rows that share the same key value within a single file. If a key repeats, only the first occurrence is used for matching; the tool tells you exactly which keys duplicated and how many, but you'll want to fix duplicates in the source file for a fully reliable comparison.\u003c\/li\u003e\n\u003cli\u003eDoes not validate that a \"price\" or \"cost\" column is really a price — any column marked for comparison that parses as a number on both sides gets a delta and percent change; anything else is compared as plain text.\u003c\/li\u003e\n\u003cli\u003eDoes not remember anything between sessions — reload the page and both loaded files are gone.\u003c\/li\u003e\n\u003cli\u003eDoes not handle Excel's native \u003ccode\u003e.xlsx\u003c\/code\u003e format — CSV only. Export from Excel\/Sheets as CSV first.\u003c\/li\u003e\n\u003c\/ul\u003e\u003ch3\u003eFeatures\u003c\/h3\u003e\u003cul\u003e\n\u003cli\u003eAuto-detects the key column (SKU, item code, product ID) from header names, with a visible warning if it couldn't confidently guess and had to fall back to the first column\u003c\/li\u003e\n\u003cli\u003eMatches rows by exact, case-insensitive key comparison — never fuzzy — so a typo'd SKU shows up honestly as one row removed and one row added, not a silently wrong match\u003c\/li\u003e\n\u003cli\u003eCompares multiple value columns in the same pass — cost, price, and quantity (or any others) — not just a single column\u003c\/li\u003e\n\u003cli\u003eComputes the dollar delta and percent change automatically for every numeric column that changed\u003c\/li\u003e\n\u003cli\u003eFlags blank\/missing values with a plain-language note instead of guessing or silently dropping them\u003c\/li\u003e\n\u003cli\u003eHandles column-order and column-name mismatches between the two files: auto-matches by header name first, with a manual override table if the headers genuinely don't line up\u003c\/li\u003e\n\u003cli\u003eDetects and reports duplicate key values and blank-key rows within a file, rather than silently merging or dropping them\u003c\/li\u003e\n\u003cli\u003eFilter results to just changed, added, or removed items; sort by key, status, or largest percent change\u003c\/li\u003e\n\u003cli\u003eExports the full diff — status, before\/after\/delta\/%-change for every compared column, plus notes — as a single CSV\u003c\/li\u003e\n\u003cli\u003eRuns entirely offline — no network requests, no analytics, nothing uploaded; tested at 6,000+ rows per file in well under a second\u003c\/li\u003e\n\u003c\/ul\u003e\u003ch3\u003eBrowser support\u003c\/h3\u003e\u003ctable\u003e\n\u003cthead\u003e\u003ctr\u003e\n\u003cth\u003ePlatform\u003c\/th\u003e\n\u003cth\u003eResult\u003c\/th\u003e\n\u003cth\u003eNotes\u003c\/th\u003e\n\u003c\/tr\u003e\u003c\/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003eDesktop Chrome (macOS)\u003c\/td\u003e\n\u003ctd\u003e**Full**\u003c\/td\u003e\n\u003ctd\u003eOpened and visually confirmed in the real, installed Chrome app (loads correctly, no console errors). The complete functional flow — load two CSVs, column\/key mapping (including manually fixing a fully-mismatched-header scenario), diff computation, filtering, sorting, and CSV export (a real file download, opened and verified in TextEdit) — was additionally run end-to-end through Chromium's real engine via Playwright, including a 6,000-vs-6,188-row performance run.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eDesktop Edge \/ Brave\u003c\/td\u003e\n\u003ctd\u003eNot separately tested\u003c\/td\u003e\n\u003ctd\u003eBoth are Chromium-based like Chrome, where the tool was confirmed Full; neither is installed on this machine, so this is an inference from the shared engine, not an independent test — not claimed as independently verified.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eDesktop Safari (macOS)\u003c\/td\u003e\n\u003ctd\u003e**Full**\u003c\/td\u003e\n\u003ctd\u003eOpened and visually confirmed in the real, installed Safari app. This tool has no Chromium-only dependency (no File System Access API, no capability gap between browsers), so the full functional flow was run end-to-end through Safari's real WebKit engine via Playwright — same diff-correctness checks as Chrome, including the large-file run (976ms compare time for ~5,988 result rows) and a verified CSV export.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eDesktop Firefox (macOS)\u003c\/td\u003e\n\u003ctd\u003e**Full**\u003c\/td\u003e\n\u003ctd\u003eOpened and visually confirmed in the real, installed Firefox app. Full functional flow run end-to-end through Firefox's real Gecko engine via Playwright, including the large-file run (698ms compare time) and a verified CSV export.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eSmall viewport \/ touch\u003c\/td\u003e\n\u003ctd\u003e**Checked**\u003c\/td\u003e\n\u003ctd\u003eNot a physical phone, but genuinely emulated (not assumed): a 390×844px touch-enabled mobile viewport (WebKit engine), with the full load → map → compare → results flow actually run through it. Confirmed no horizontal overflow (`document.body.scrollWidth` matched the viewport width exactly).\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eiOS Safari (iPhone\/iPad)\u003c\/td\u003e\n\u003ctd\u003e**Not verified**\u003c\/td\u003e\n\u003ctd\u003eNo physical iOS device available. iOS Safari shares desktop Safari's WebKit engine and this tool has no capability gap to begin with, so the same behavior is expected — but this is an inference, not a test on real hardware, and is not claimed as verified.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eAndroid Chrome\u003c\/td\u003e\n\u003ctd\u003e**Full**\u003c\/td\u003e\n\u003ctd\u003eTested on a real Android device in Chrome — works.\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003c\/tbody\u003e\n\u003c\/table\u003e\n\u003cp\u003e\u003cem\u003e**Method note on \"automated testing\":** beyond opening each real installed browser app and visually confirming it loads cleanly, the functional flow (CSV parsing, column\/key mapping including manual override of a mismatched-header scenario, numeric delta\/%-change computation, duplicate-key and blank-key-value handling, filtering, sorting, CSV export as a real file download) was exercised end-to-end using Playwright driving each engine's own real Chromium\/WebKit\/Gecko binary — the same unmodified code, actually run, not read from source and assumed. A 20-row Before \/ 20-row After test pair — with 9 genuinely changed rows across three value columns, 3 added, 3 removed, 8 unchanged, one deliberately typo'd key (a letter \"O\" swapped for a zero, so it should *not* match), and one deliberately blank value — was hand-calculated first (every delta and percent change worked out by hand), then run through the tool; every single value matched the hand-calculated expectation exactly, in all three engines, with zero console errors.\u003c\/em\u003e\u003c\/p\u003e\u003ch3\u003eFAQ\u003c\/h3\u003e\u003cp\u003e\u003cstrong\u003eDoes my price list get uploaded anywhere?\u003c\/strong\u003e\u003cbr\u003eNo. The entire tool is one static HTML file with no server component and no network calls. Every row is parsed and compared locally in your browser tab. You can open it with your Wi-Fi off, or read the page's source yourself to confirm there's nothing that sends data anywhere. (Worth knowing: several free online CSV-diff tools do run entirely client-side too — this isn't a claim unique to this tool — but a few require uploading your file to their server to process it, and even the client-side ones are hosted web apps you have to trust and revisit each time, not a file you keep.)\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eCan I compare more than just one price column?\u003c\/strong\u003e\u003cbr\u003eYes. Mark as many columns as you want to compare — cost, price, quantity, or anything else — and each gets its own before\/after\/delta\/%-change in the results and the exported CSV.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eDoes this handle different column orders or column names between the two files?\u003c\/strong\u003e\u003cbr\u003eColumn order doesn't matter at all — columns are matched by header name, not position. If the two files use genuinely different header names for the same data (e.g. \"SKU\" vs. \"Item Code\"), the auto-match won't find it automatically, but the mapping screen lets you manually pair up any Before column with any After column before comparing.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eWhat happens if a SKU is typo'd or slightly different between the two files?\u003c\/strong\u003e\u003cbr\u003eIt will not be matched — this tool only matches on an exact (case-insensitive) key, deliberately, so it never silently guesses that two different-looking values are \"close enough.\" A typo'd key shows up as one row removed (from the old file) and one row added (in the new file), which is usually the honest, useful signal that something needs a second look.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eCan I use this for client or commercial work?\u003c\/strong\u003e\u003cbr\u003eYes — personal and commercial use are both fine.\u003c\/p\u003e\u003cp\u003e\u003cstrong\u003eIs there a subscription?\u003c\/strong\u003e\u003cbr\u003eNo. One-time purchase, yours to keep and reuse forever.\u003c\/p\u003e","brand":"My Store","offers":[{"title":"Default Title","offer_id":54670848033105,"sku":null,"price":9.0,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/1073\/4226\/6705\/files\/01-hero_813791b3-7ff4-4b23-b7b3-7c46a8793e14.png?v=1787610521"}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/1073\/4226\/6705\/collections\/01-hero_813791b3-7ff4-4b23-b7b3-7c46a8793e14.png?v=1787659722","url":"https:\/\/untracked.tools\/collections\/data-cleanup.oembed","provider":"Untracked Software","version":"1.0","type":"link"}