A few days ago I had a simple, slightly weird idea:

What if every string was just an array of single letters… and I could rebuild words, paths, and API links by pointing at index ranges?

That experiment turned into string-range-tokens (srTokens).

It’s a tiny, zero-dependency ESM package that does one thing well:

Turn any string into a letter-per-index token array, then reassemble pieces of it with clean range calls.

No regex. No string slicing gymnastics. Just indexes.

The 30-second version

import { toTokens, sub, build } from 'string-range-tokens'; const t = toTokens('https://api.example.com/v1/users'); sub(t, 0, 4); // "https" sub(t, 23, 25, [26, 31]); // "/v1/users" sub(t, -5, -1); // "users" build(t, [0, 4], [23, 25]); // "https/v1" 

That’s the core. Everything else is built on top of it.

What you can actually use it for

1. Assemble API paths from a shared pool

Instead of hard-coding every endpoint, keep one master string and pull the parts you need:

const pool = toTokens('https://api.myapp.com/v1/users/orders/products/settings'); const users = build(pool, [0, 22], [23, 27]); // https://api.myapp.com/v1/users const orders = build(pool, [0, 22], [29, 34]); // https://api.myapp.com/v1/orders const products = build(pool, [0, 22], [36, 43]); // https://api.myapp.com/v1/products 

Useful when you want a single source of truth for the base URL + version and then compose endpoints on the fly.

2. Build words from a letter pool (like a tiny anagram engine)

const letters = toTokens('the quick brown fox jumps over the lazy dog'); const ranges = plan(letters, 'fox jumps'); build(letters, ...ranges); // "fox jumps" plan(letters, 'lazy dog'); // finds the ranges plan(letters, 'brown'); // finds "brown" 

plan() does a greedy longest-match search and returns the ranges that reconstruct the target. If a character is missing, it throws. Great for puzzles, teaching, or constrained string generation.

3. Negative indexes for “from the end” slicing

const t = toTokens('invoice-2026-10-09-final.pdf'); sub(t, -9, -1); // "final.pdf" sub(t, -14, -10); // "10-09" sub(t, 0, 6, [-9, -1]); // "invoice-final.pdf" 

Feels natural once you start thinking of the string as a fixed array.

4. Scatter & gather — deterministic scramble

Sometimes you want to shuffle a string in a repeatable way and still be able to put it back together:

const { pool, key } = scatter('/api/private/orders', 42); console.log(pool.join('')); // something like "r/paeivtr/o/psordo" (order depends on seed) gather(pool, key); // "/api/private/orders" ← perfect round-trip 

Same seed always produces the same shuffle. Handy for demos, tests, or playful obfuscation (remember: this is not security).

5. Tiny templating from a fixed alphabet

const alphabet = toTokens('abcdefghijklmnopqrstuvwxyz0123456789-_.'); // build a fake ID const id = build(alphabet, [0, 0], [1, 1], [2, 2], [10, 10], [26, 26], [27, 27] ); // "abc k01" → wait, better with plan or explicit ranges // or more expressively: const ranges = plan(alphabet, 'user-42'); build(alphabet, ...ranges); // "user-42" 

You can keep a single character pool and generate many different strings from it.

6. Teaching indexing and ranges

Because everything is explicit indexes, it’s surprisingly good for explaining:

  • zero-based indexing
  • inclusive ranges
  • negative indexing
  • how Array.from handles Unicode
toTokens('a😳b'); // ['a', '😳', 'b'] — emoji is one token sub(toTokens('hello'), 1, 3); // "ell" 

Students can see exactly which characters are being selected.

Full API at a glance

Function What it does
toTokens(str) String → array of single characters (Unicode-safe)
sub(tokens, s, e, …) Join one or more inclusive ranges
build(tokens, …ranges) Same idea, ranges-only style
plan(tokens, target) Find ranges that rebuild target
scatter(str, seed) Seeded shuffle → { pool, key }
gather(pool, key) Reconstruct original from scatter output

All functions are pure. Zero dependencies. Works in Node 18+, Deno, Bun, and modern browsers.

TypeScript types are included, and you can even import the .ts source directly if your runtime supports it.

Install

npm i string-range-tokens 

Or skip the install and just copy src/index.js — it’s a single file.

CDN:

import { toTokens, sub, build, plan, scatter, gather } from 'https://cdn.jsdelivr.net/npm/string-range-tokens@1.0.0/src/index.js'; 

Important note about “hiding” strings

This is string assembly, not encryption.

Anything that ends up in the browser can be read by the user. Never try to hide real API keys, tokens, or secrets with it. Keep those on the server (Node Functions services, Cloudflare Workers, etc.).

Use srTokens for clarity, composition, teaching, and fun — not for security theater.

Links

If you build something interesting with it — a path builder, a puzzle, a teaching demo, or something completely unexpected — I’d love to see it.

Happy ranging.