Run-length encoding and decoding tool
«aaabbcccc» encodes to «a3b2c4», and decoding «a3b2c4» gives back «aaabbcccc», letter for letter. Digits and backslashes are escaped so every result decodes without ambiguity, and any Unicode character works. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Text
- aaabbcccc
- Calculation mode
- Encode
- Result
- a3b2c4
Run-length encoding turns consecutive copies of a character into one character followed by the number of repetitions. This tool performs that transformation in either direction: choose encode for ordinary text or decode for a previously encoded value. It handles Unicode characters, preserves exact character order, and uses a small escaping rule so digits and backslashes can round-trip without ambiguity.
Encode consecutive runs without losing their order
Choose encode and provide the source in the text field. The encoder reads Unicode characters from left to right, groups only adjacent equal characters, and writes each group as its character followed by a decimal count. For example, a block of three identical letters becomes that letter and the number three.
Equal characters separated by another character remain separate runs, because run-length encoding describes local repetition rather than global frequency. Empty input produces an empty result, and a character appearing once receives a count of one. Digits and backslashes need special care because counts are also written with digits.
The format therefore prefixes a literal digit or literal backslash with a backslash before adding its count. This escape makes every result parseable while preserving the familiar character-then-count structure for ordinary text. The response includes the selected mode and result, so callers can route or record conversions without guessing which operation occurred.
Decode with strict format validation
Choose decode when the text already contains run-length tokens. Each token must contain exactly one character representation followed by a positive decimal count. Ordinary non-digit characters need no prefix.
A digit character or backslash character must be escaped with a preceding backslash, which distinguishes literal data from the count that follows it. Counts cannot be zero, cannot begin with zero, and must fit safely within JavaScript integer precision. The decoder also rejects an unescaped digit at the start of a token, a dangling backslash, an unsupported escape, or a character with no following count.
The decoded output is capped at one million Unicode characters, preventing a short input with an enormous count from consuming excessive memory. Input itself is capped at one hundred thousand Unicode characters. Because validation occurs during parsing, applications receive a useful stable message near the violated rule and never receive a partial decoded result.
Use run-length encoding where repetition is meaningful
Run-length encoding is easiest to understand as a reversible representation, not a universal compression method. It works especially well when data contains long uninterrupted runs, such as simple masks, repeated status markers, grid rows, test patterns, or introductory compression exercises. It can make alternating or mostly unique text longer because every run includes a count; this tool does not hide that tradeoff or choose a different compression scheme automatically.
For production workflows, store or transmit the returned result together with knowledge of this documented escape convention, since another run-length implementation may use separators, brackets, or count-first tokens instead. Unicode is processed by code point rather than UTF-16 code unit, so common emoji and supplementary characters are treated as individual characters.
What you can do with it
Teach a basic compression algorithm
Show how adjacent runs become character-count tokens, then decode the result to demonstrate reversibility and validation.
Validate encoded input
Reject malformed run-length strings before they enter a pipeline, with explicit errors for bad counts and escapes.
FAQ
How are digits encoded?
A literal digit is prefixed with a backslash and then followed by its count. This prevents the digit from being confused with a run count.
Can it encode emoji and other Unicode characters?
Yes. The algorithm iterates over Unicode code points, so common supplementary characters such as emoji are counted as individual characters.
Why does decoding reject a count that begins with zero?
The canonical format uses positive integers without leading zeros. Rejecting alternate spellings keeps encoded values predictable and catches likely mistakes.
Will run-length encoding always make text shorter?
No. It is effective for long consecutive repetitions, but text with short or alternating runs can become longer because every run includes a count.
Are repeated letters that are not side by side combined?
No. Only adjacent equal characters form a run. In «abab» each letter is its own run, so the result is «a1b1a1b1», longer than the original. Run-length encoding describes local repetition, not how often a character appears overall.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/text/run-length-encode \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"aaabbcccc"}'const res = await fetch("https://api.kit.forhosting.com/text/run-length-encode", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "aaabbcccc"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/text/run-length-encode",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "aaabbcccc"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/text/run-length-encode", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"aaabbcccc"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"aaabbcccc"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/text/run-length-encode", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"text": "aaabbcccc"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "text.run_length_encode",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_tokens | 20000 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
Encode consecutive runs without losing their order
Processing is deterministic: the same text and mode always return the same JSON value.
Decode with strict format validation
These rules intentionally fail closed: malformed text returns an invalid_input error instead of producing a plausible but incorrect value.
Integration use cases
Create compact repetitive fixtures
Represent long runs in masks, grid rows, status markers, or synthetic test data with a small deterministic string.
Technical questions
What does the API request cost?
Each API item costs $0.002. The browser runner can perform the same deterministic transformation locally.