DEV Community

Cover image for Sorry 100 times generator
Sonny kk
Sonny kk

Posted on

Sorry 100 times generator

How I Built a Text Repeater That Doesn't Freeze the Browser at 10,000 Repetitions

A few months back I shipped a small utility that does one thing: repeat a block of text however many times you want. Sounds trivial, right? String repetition is a one-liner in most languages. But the moment I put a "10,000" option in the UI, I ran into a handful of problems that taught me more about browser performance than I expected from something this small.

This is a build log, not a highlight reel. I want to walk through the actual technical decisions, including the ones that didn't work the first time.
Before going further see some example output of the tool-
I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺 I am really sorry 🥺

The Trigger: A Weird Keyword

Before any code, there was a keyword. I was doing search research for a side project and noticed a strange pattern: thousands of people searching sorry 1000 times every month. Not a typo, not a one-off — a consistent, recurring search. People wanted a fast way to generate a huge block of repeated text, usually the word "sorry," and copy or download it immediately.

That's a product decision hiding inside a search query. So I decided to build it properly, as a developer, not just slap together a script.

Problem 1: Naive String Repetition Doesn't Scale the Way You Think

My first pass looked roughly like this:

function repeatText(text, count) {
let result = "";
for (let i = 0; i < count; i++) {
result += text + "\n";
}
return result;
}

At 100 repetitions, fine. At 1,000, still fine. At 10,000, this starts to hurt — repeated string concatenation in a loop creates a lot of intermediate string allocations, and depending on the engine, that can get expensive fast. The fix was obvious once I looked at it properly: build an array and join it once.

function repeatText(text, count) {
return Array(count).fill(text).join("\n");
}

Massive improvement. Lesson: don't concatenate strings in a hot loop, ever, even for something as "simple" as a repeater tool.

Problem 2: Rendering 10,000 Lines to the DOM Is the Real Bottleneck

Generating the string was fast. Displaying it wasn't. Dumping 10,000 lines into a <textarea> or a rendered

caused visible jank on lower-end devices, especially mobile. The DOM doesn't care how efficient your string logic is if you're forcing a massive reflow.

The fix here was to keep the raw text in a <textarea>, which browsers handle far better than rendering thousands of individual DOM nodes (which is what would happen if I'd foolishly rendered each repetition as its own element, something I actually tried first and immediately regretted). A <textarea> treats it as plain text content, not a DOM tree, so it stays lightweight even at 10,000 lines.

Problem 3: Copy and Download Needed to Feel Instant

Since the whole point of this tool is speed — someone searching "sorry 100 times" wants their text now, not after five seconds of spinner — I had to be careful with the copy and download flows too.

For copying, I used the Clipboard API directly instead of the older document.execCommand("copy") fallback, since it's cleaner and doesn't require selecting text manually:

async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
} catch (err) {
console.error("Copy failed", err);
}
}

For downloading, I generate a Blob client-side and trigger a download without ever touching a server:

function downloadAsTxt(text, filename = "repeated-text.txt") {
const blob = new Blob([text], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}

No backend call, no waiting, no server round-trip. Everything happens in-browser, which matters a lot when your entire value proposition is speed.

Problem 4: Debouncing the Generate Action

Early testers (mostly friends I roped into clicking buttons for me) kept mashing the generate button impatiently, especially at the 10,000 setting, thinking it hadn't registered. I added a short debounce and a lightweight "generating..." state so the UI gives immediate feedback even though the actual generation takes milliseconds. Perceived performance matters as much as real performance.

What I'd Tell Another Developer Building Something Similar

If you're building anything that takes a "how many times" input from a user, don't assume they'll stick to reasonable numbers. Someone will always test the upper limit immediately — in my case, that's literally the point, since people specifically search for the highest counts like "sorry 1000 times" or more. Build for the ceiling, not the average case.

Also, resist the urge to over-engineer a tool this small. I didn't need a framework, a state management library, or a backend. Vanilla JS, a textarea, the Clipboard API, and the Blob API covered everything. Sometimes the right architecture is just... less architecture.

If you want to see how the copy and download flow feels in practice, the tool supports presets too, so you can test type sorry 100 times copy and paste style output in a couple of clicks without typing anything yourself. I'd genuinely appreciate feedback from other devs on what would break this at scale — I'm always looking for the next edge case I haven't hit yet.

Top comments (0)