HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
pieroxy
lz-string
TypeScript2017–2023
pieroxy / lz-string

lz-string

LZ-based compression algorithm for JavaScript

8Distinct people
14Mentions
0Explicit recommenders
4.4KGitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

LZ-based compression algorithm for JavaScript

GitHub

Where it is used

No documented product use has been verified yet.

What people are saying

No recommendation phrase was found in the indexed comments.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

No other repository is repeatedly linked in the same discussions.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 3:03:16 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year14 dated mentions

Observed mentions: 2017-05-02 21:04:56 UTC to 2023-10-17 14:47:27 UTC.

012342017-01-01 00:00:00 UTC to 2018-01-01 00:00:00 UTC (exclusive): 3 mentions; 0 submissions, 3 comments20172018-01-01 00:00:00 UTC to 2019-01-01 00:00:00 UTC (exclusive): 4 mentions; 0 submissions, 4 comments2019-01-01 00:00:00 UTC to 2020-01-01 00:00:00 UTC (exclusive): 3 mentions; 0 submissions, 3 comments2020-01-01 00:00:00 UTC to 2021-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments20202021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 2 mentions; 1 submissions, 1 comments2023

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20172017-01-01 00:00:00 UTC2018-01-01 00:00:00 UTC303
20182018-01-01 00:00:00 UTC2019-01-01 00:00:00 UTC404
20192019-01-01 00:00:00 UTC2020-01-01 00:00:00 UTC303
20202020-01-01 00:00:00 UTC2021-01-01 00:00:00 UTC202
20212021-01-01 00:00:00 UTC2022-01-01 00:00:00 UTC000
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC000
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC211

What Hacker News says

14 sources
not really niche yet but good starting point to branch off to niche tools! You should definitely consider adding save to / load from localStorage, IndexedDB, or URL. Nice thing about URL is you can send pages to other people like this: https://unli.xyz/tabsender/?links=%22open%20a%20bank%20accou... Or use LZString https://github.com/pieroxy/lz-string like this: https://unli.xyz/html/edit.html?t=%22DwBwfALghgTgXlAdsA9OYAz... An accidental page refresh shouldn't eliminate all your TODOs :/
In Ask HN: What niche task/tool websites have you built?
So for about two years I've been bouncing around an idea in my head to make the LZ78 compression algorithm (or more specifically, the LZString variant of it) compress better by forgetting the least commonly used substrings. I think I'm just reinventing a mixture of LZC and LZAP (or LZMW), but the on-line literature discussing either algorithms in detail is abysmal[0][1][2]. The main innovation would be that I have a fairly simple way to implement the forgetfulness. Another separate innovation that I have in mind is combining that with a special run-length encoding sequence that, due to the interaction between LZAP and run-length encoding, actually wouldn't encode runs linearly but exponentially[3]. That is, instead of "repeat substring X for N times" it would say "repeat substring X for fibonnacci(N) times". I suspect that might actually
Read the rest
be a novel innovation! Aside from a lack of time and energy, the main thing holding me back has been, and I'm dead serious, off-by-one errors. It's really, really easy to screw up the order of adding new entries to the dictionary on both the compression/decompression side in such a way that you just get garbage out, and it's a pain to debug. If my algo turns out to be original, I'm tempted to call it LZWAN, or Lempel Ziv With Amnesiac Nodes (referring to the nodes in the trie used to grow the dictionary). [0] https://en.wikipedia.org/wiki/LZ77_and_LZ78 [1] https://pieroxy.net/blog/pages/lz-string/index.html [2] https://ethw.org/History_of_Lossless_Data_Compression_Algori... [3] https://github.com/pieroxy/lz-string/issues/114#issuecomment...
In Ask HN: What's your quarantine side project?
> I'm not confident enough to "roll my own" For local net, setting up pi-hole is do-able, whilst setting it up over a VPN involves a bit of additional tooling. A 100% uptime DNS on public internet is tricky, but if you're okay with just DoH [0], the https://workers.dev free-tier would you give you a 100% uptime "anycast", low-latency DoH enough for a single device's worth of queries for a month. At $5, you'd have enough for a 100 devices. I've one setup with adblock forwarding queries to 1.1.1.1 and I see e2e latencies as low as 50ms regardless of location. This setup is a bit involved: You'd need to convert blocklists (the one I use has a million entries) to either a long-string, custom LZ-compress it (~6 MiB) [1] and do a boyer-moore needle-haystack search on it for incoming questions (v8's native String#index impl uses a variant of bo
Read the rest
yer-moore) [2], or use a json-map at a cost of higher RAM usage but blazing-fast lookups (~25 MiB), or use succinct radix-trees for optimal RAM usage (~1.5 MiB) but relatively slower lookups, or use bloom-filters with low false-positive but fast membership queries at extremely frugal RAM usage (~200 KiB) [3]. That said, the simplest way to ad-block would be to point the workers instance to adguard-doh. [0] https://news.ycombinator.com/item?id=21598413 [1] https://github.com/pieroxy/lz-string/ [2] https://stackoverflow.com/questions/5562297/fast-search-in-c... [3] https://news.ycombinator.com/item?id=22017868
In OpenSnitch is a GNU/Linux port of the Little Snitch application firewall
The 1 MB limit bug report gets on a certain nerve of mine, "I don't think it'd be fast so no need to change the limit just do these things that'll make it slower anyways". There might be an easy way around all of this though, the problem is that the URLs used START encoded. If you just sourced the raw list and in the extension live ran it through https://github.com/pieroxy/lz-string/blob/master/libs/lz-str... to output the lz compressed pac file to disk (you'll need the filesystem write permission on the extension) and attach that lz-string library at the end and call it to decompress (similar to how you do now just not manual) it's probably a win-win. I say win-win because on your side the lz compression will probably net a file ~10x smaller than what you have now at the same decode speed and on Google's side the URLs used are stored a f
Read the rest
orm they can validate against.
In Anti-censorship Chrome extension removed for custom minification
That won't fit in cookie space... this is really for redundant controls for session/access tokens etc... anything large like that you're better off using caching and a service worker to handle repeated requests. Also, cookies are sent with every request to the server, and I don't think you'd want the user sending 10MB to your server for every request. Also, if you are storing larger strings in local/sessionStorage, websql or indexdb, you should use lz-string[1] to try to minimize storage impact. Aside: if you want something with a nicer interface for underlying storage you may want to look at pouchdb[2], which has a localized couch interface and supports remote synching to coudchdb, which is pretty nice. [1] https://github.com/pieroxy/lz-string [2] https://pouchdb.com/
In ImmortalDB – A resilient key-value store for the browser
> The original prototype was to develop an idea I had about using jump indices in a TypedArray for quickly matching hostnames (or more generally strings)[1]. Once I had a working, un-optimized prototype, I realized I had ended up with something formally named a "trie": <https://en.wikipedia.org/wiki/Trie>, hence the name. I have no idea whether the implementation here or one resembling it has been done elsewhere. So I heavily experimented with squeezing performance out of a JS-based trie-implementation for LZString[0]. Back then some early experimentation suggested that using TypedArrays was likely to be slower than using objects with numerical keys, and for non-degenerate input using a trie based on plain arrays and indexOf calls was the fastest[1]. However, it's been a while since I looked at those numbers (and I suspect that with uBloc
Read the rest
k's use case look-up performance matters more than insertion performance, which makes it very different from LZString's). But it should be obvious that HNTrie's implementation[2] is something I'm very interested in ;). [0] https://github.com/pieroxy/lz-string/pull/98 [1] https://github.com/pieroxy/lz-string/blob/ba8988028d78962eba... [2] https://github.com/gorhill/uBlock/blob/2a91a685ce3d2dae5d3c2...
In UBlock Origin 1.17.4 released
> The code makes use of localStorage() to save the raw text and the time and date of when the edit has been made. Given that localStorage only has 5MiB available to it, you could try using the localForage library to have a localStorage-like API around IndexedDB, so that people can store even larger notes (you could even support storing pasted images as PNG blobs). Even better: is has automatic localForage fallback. Alternatively, you might want to either compress the text with LZ-String before storing it[1]. (BTW, version 2.0 is currently in the works and much faster, especially if you use the "Unsafe" version[2]) [0] https://localforage.github.io/localForage/ [1] http://pieroxy.net/blog/pages/lz-string/index.html [2] https://github.com/pieroxy/lz-string/pull/98#issuecomment-40...
In Show HN: Markdown New Tab – A new tab replacement to jot down notes in Markdown
This (and the older blog-post about backing stores[0]) are incredibly relevant to my LZ-string optimisations[1]. LZ-string takes an input string, builds a dictionary of substrings as it scans over the input, and outputs substring-tokens to a bit-stream, then converts that bit-stream into a new string. How that dictionary is built is what is most relevant here: it is the greatest bottleneck in determining the speed of the algorithm. I have tried many variants. The ones that seem to work best match what is described here: - instead of naively using sub-strings and dictionary lookup, build a trie[2] (putting all sub-strings in an object used as a flat dictionary quickly overflows the 1022-elements limit) - instead of character strings, use charCodeAt[0] to use integer keys in the look-up of the trie (index with elements rather than hashcodes
Read the rest
, and because JS strings are UTF16, the keys are SMI. It also removes string creation overhead). - for a long time, I used one object for each node, with 0 as the key to look up its value, and charCode+1 to look up next nodes. In recent benchmarks it turned out that { v: val, d: {} } is faster[4][5][6]. This makes sense because: "we don't store information about present indexed properties on the HiddenClass" [0]. In other words: the new options creates a distinct hidden class compared to a plain object, which might be optimised. At the same time adding integer keys to a separate d object will not mess with this hidden class, preventing de-opts. - for really fast performance except in a few degenerate cases, instead of key look-up, use an array with linear search (yes, really! Turns out compact arrays without holes are faster to the point where even linear search makes it worth it - up to a certain length). So this explains a lot of why certain approaches work better than others. Some of the explanations here might let me optimise it even further, but at this point it would become hyper-optimized for V8. Are there some take-aways that I can generalise? And is there a similar blog for the other big javascript engines? I expect that "type stability" - that is, maintaining identical hidden classes - is fairly universal for example. [0] https://v8project.blogspot.se/2017/08/fast-properties.html [1] https://github.com/pieroxy/lz-string/pull/98 [2] https://en.wikipedia.org/wiki/Trie [3] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... [4] https://run.perf.zone/view/LZ-String-2018-01-18-v2-151619850... [5] https://run.perf.zone/view/LZ-String-2018-01-18-v1-151619752... [6] https://github.com/pieroxy/lz-string/pull/98/commits/1894f7c...
In Optimizing hash tables: hiding the hash code
It doesn't have to be a full project, right? Do random drive-by PR-requests to open-source projects count? A few months ago I ended up scratching an optimisation itch for weeks, trying to figure out ways to make the lz-string[0][1] library faster and smaller. Near the end I went a bit nuts with trying out what works, methinks (nested trees built out of arrays and such), but I had a lot of fun. It's not even my library, nor did my PR request get accepted/rejected yet. It did however make the compression up to 2x to 10x faster, depending on how well the data compresses. And hey, I now have an intuitive understanding of LZ compression that I never thought I'd have! Since a few days I've been working on writing a component for idyll[2] that lets you embed p5js sketches[3]. Progress here[4][5]. [0] http://pieroxy.net/blog/pages/lz-string/index
Read the rest
In Ask HN: Projects that don't make you money but you're doing it out of sheer joy?
It just so happens that I've been working on trying to build the fastest possible trie implementation in JavaScript to speed up `lz-string` and potentially `js-search`. The whole process I went through is too much to write down here, but you can basically read it here: https://github.com/pieroxy/lz-string/pull/98 https://github.com/bvaughn/js-search/issues/42 The short version is: replace those single-char strings with the result of `charCodeAt()`, and then use `charCodeAt()` during search too. The reason is that integer key lookup is much faster than string key lookup: https://stackoverflow.com/questions/10639488/faster-to-acces... It's important that all of your keys are integers though, so the trick is to actually store/look up `charCodeAt()+1`, and then store the indices at key 0. If you feel like taking a risk, you can make a slightl
Read the rest
y more complicated data structure where you replace the object with an array and do a linear search: https://jsfiddle.net/vanderZwan/ccfxv4sg/1/ For most data this is the fastest, because the array will only be a handful of characters (except at the root). However, in certain special cases (specifically, if you have a lot of unique characters at the root) it becomes prohibitively slow.
In Inside Wade, a 1kb search library
If you use React with react-router[0] (or a comparable JS-based solution), you can just apply lz-string[1] to a stringified JS object and make the resulting string match a path. Then you need is some code to reverse the operation (lz-string -> JSON -> JS object). That's what I'm doing to create shareable state in an app I'm building for a research group. Done naively, it will have the same problems others mentioned here: it barely shortens the URL. However, in the case of my specific app - a browser for data sets for single cell RNA sequencing - the state is largely dependent on the data set being viewed, and the data set is unchanging. What that means is that we can "externalise" the data being referred to, to pre-transform my JS object (which is an object tree) to nested arrays with integers (so a kind of "array tree") that use the data
Read the rest
set as a look-up to reverse the operation. The latter is already a lot shorter when stringified, but also more compressible: the character set is limited to ten digits, commas and square brackets, and different values may be transformed into the same numbers, being distinguished by their position in the array tree. To make this operation easier, I created a few helper functions to declare a "schema" that creates a recursive function for transforming the original JS object to said array of arrays, and vice-versa: https://gist.github.com/JobLeonard/a47692a1f77bebc06c2518f32... As you can see in the gist, that transformation shrunk a URL of 2466 characters down to a (admittedly still crazy) 638 characters. I was thinking on putting it on NPM but it looked so specific in its applicability that I haven't bothered turning it into a proper package yet. [0] https://github.com/ReactTraining/react-router [1] https://github.com/pieroxy/lz-string
In Show HN: Shortern URIs using Huffman Coding, not a database

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.