CN All projects
Polski
Let's talk

TuMedytuj

A meditation written for one situation.

TuMedytuj creates a meditation, both the text and the voice, for the situation someone is in right now. Not another generic recording from YouTube. It's the first tool like this in Polish that I've come across.

I built it myself in about two weeks, from September 7 to 19, 2026. It's a single-page app with the meditation tool built in, deliberately without user accounts or a separate SaaS back end. It runs on its own domain, tumedytuj.pl.

The TuMedytuj meditation card: the question What are you dealing with today, a field for your own words, suggestions such as I can't fall asleep and Pressure at work, a session length choice with only the 5-minute option unlocked, the Wygeneruj medytację (Generate meditation) button and a counter saying 5 of 5 left this month
The meditation tool on the live site, October 6, 2026. Longer sessions are locked because they belong to top-up packs that aren't on sale yet. The product is in Polish; interface labels quoted on this page are translated.
Role
Product design and front-end
Time
About two weeks
Stack
Next.js, Claude, ElevenLabs
Status
Live at tumedytuj.pl

Starting point

The cost of one session isn't fixed.

That changed everything before I designed the first screen.

I didn't start with the interface but with a question that turned out to be the most important decision in the whole project: how much does it actually cost to create one meditation, and can that be covered by the model everyone in this category uses?

Competitors run on a pure subscription: a fixed monthly fee for access. At first I went the same way, until I worked out what one meditation really costs.

The cost of voice synthesis grows linearly with the length of the recording, because ElevenLabs bills per character. With a “subscription = unlimited access” model you can't calculate the margin in advance, because a handful of very active users can eat up the profit made by everyone else.

When the cost of a single use varies a lot, around 8% of users can account for around 61% of costs in a subscription model.

I found an article by Freemius from the same month that confirmed it. This wasn't “sales could be better”. It was a concrete, calculable cost that had to shape the product's architecture before I started designing anything visual.

Pricing from September 8, 2026: a Free plan and a dark Pro card at 19 zł a month with a Coming soon label, listing Longer 10 and 15-minute sessions, Unlimited meditations, and Saving and your own library
Enlarge frame
September 8, 2026, before the decision Pro at 19 zł a month

“Unlimited meditations” for a fixed price, with a cost that grows with every minute of recording.

Pricing today: a Free plan, the heading Top-ups, not a subscription, a bar showing 5 min = 1 unit, 10 min = 2, 15 min = 3, and three packs: First Step 19 zł, Ritual 45 zł and Everyday 79 zł
Enlarge frame
Today on tumedytuj.pl Top-ups in units

5 minutes is one unit, 10 minutes two, 15 minutes three. A shorter meditation uses less, a longer one more.

The left frame is the site from commit d722498 (September 8, 2026, 11:29 pm), restored from git history and run locally. The right one is the live site from October 6, 2026. Payments aren't live yet in either version.

I switched the model to top-ups. During the build I refined it once more: instead of counting every meditation the same, I moved to units based on the length of the recording. That one decision forced changes in at least five places on the site. I wrote up each of them separately, with a reason, not in bulk.

  1. 01Session length control
  2. 02Pricing
  3. 03FAQ
  4. 04Hero
  5. 05Form microcopy

Diagnosis

The site promised honesty.

In four places, it didn't deliver.

Before anyone noticed, I audited the content of the whole site against what the code actually did. I found four mismatches at once. The FAQ promised five free meditations a month “with no asterisks and no catches”, next to an undisclosed limit of two audio plays. The hero promised a choice of session length that the free plan didn't offer. The pricing didn't mention a feature that already existed. And the paid packs advertised a choice of voice that was actually free for everyone, a leftover from a plan I had dropped earlier.

To me this was a more serious problem than an average-looking site. The site openly declared transparency as a brand value, and in four places promised something the product didn't deliver. I fixed it not because someone reported it, but because declared honesty stops being true without regular audits of the code.

Hero

A promised choice the free plan didn't offer

The facts below the hero button before the audit: 5 / 10 / 15 min, Choose the length; 5 meditations, Free every month; 0 zł, No sign-up
Before the audit“5 / 10 / 15 min, Choose the length”, even though only 5 minutes was available without a pack.
The facts below the hero button today: 5 meditations, Free every month; 2 plays, Voice with background music; 0 zł, No sign-up
TodayThe length choice is gone, and in its place is the limit: “2 plays, Voice with background music”.
FAQ

“No asterisks” next to an unmentioned limit

The question How much does it really cost before the audit, answer: Five meditations a month cost nothing. No asterisks and no catches.
Before the auditNot a word about only two of the five meditations being playable aloud.
The same question today, answer: Five meditations a month cost nothing, and you can listen to two of them aloud. No asterisks and no catches.
TodayThe limit sits in the same sentence as the promise, so “no asterisks” is true again.
Pricing

A free plan missing a feature that already existed

The Free card before the audit: four points, 5 personalized meditations a month, A technique matched to your problem, 5-minute sessions, No sign-up and no ads
Before the auditFour points. Neither listening aloud nor background music, even though both already worked.
The Free card today: five points, including you can listen to 2 of them aloud, with background music
TodayThe fifth point mentions both the feature and its limit: “you can listen to 2 of them aloud, with background music”.
Packs

A paid feature that was free

Packs before the audit: a bar saying Any length, 5, 10 or 15 minutes, Voice choice, female or male, No expiry date; below it First Step, Ritual and Everyday counted in meditations
Before the audit“Voice choice: female or male” as a pack benefit, even though everyone chose the voice, free users included. A leftover from the Pro plan.
Packs today: a bar saying 5 min = 1 unit, 10 min = 2, 15 min = 3, Every meditation playable aloud, No expiry date; below it the same three packs counted in units
TodayThe bar says what a pack really adds: listening to every meditation aloud. Prices are in units, not meditations.

The “before the audit” state is the site from commit 68488d2 (September 9, 2026, 1:40 pm), restored from git history. I closed all four mismatches in one commit on September 9 at 2:21 pm, together with the move to units.

Honesty as a product feature, not a slogan on the site.

From this diagnosis I drew a rule I came back to with every later decision: honesty should be something the product does, not something it says about itself. I rejected fabricated social proof: zero testimonials, media logos or made-up user counts. For a small product, I built authenticity differently than with the standard set of growth patterns.

The bar of shared features above the pricing first had a tile with a filled background. I rejected that version because I recognized in it exactly the template AI-SaaS look that my own design-system.md tells me to avoid, and kept the thin lines the rest of the site is built on.

  1. Analytics consent

    A banner that really asks

    If you decline, the Vercel Analytics script doesn't load at all, rather than just stopping data collection. Both buttons, accept and decline, carry the same visual weight, because I called a smaller or hidden decline button what it is, a dark pattern, not a matter of hierarchy.

    The No cookies banner: I collect anonymous visit statistics to know whether this site helps anyone. Two buttons of the same width: Fine by me and I'd rather not
  2. Session length

    A statement instead of a question

    I labeled the session length control with the statement “Your session will last” rather than the question “How much time do you have?”, because a question the product can't honestly answer with a choice reads like a promise.

    The control before the change: the heading How much time do you have?, 5 minutes active, 10 and 15 minutes with a padlock
    Before
    The same control today: the heading Your session will last, 5 minutes active, 10 and 15 minutes with a padlock
    Today
  3. Voice choice

    The limit sits where the decision is made

    When choosing a voice, the site says how many plays are left and that the choice is final, before anyone clicks. It's the same limit that was missing from the FAQ before the audit.

    A choice of Female voice or Male voice, a Listen to a sample of this voice link, a Play aloud button and a note: You have 1 of 2 plays left this month. The voice you choose stays with this meditation, so listen to the sample before deciding.

Method

Measure, don't guess.

The same sentence came back with every decision.

This pattern runs through the whole changelog, not just the voice or the pricing. I measured the color contrast of the highlighted column in the comparison table and got 1.07:1, at the edge of what's visible. I raised it to 1.32:1 with a new color token, not by changing opacity.

The decorative graphics in the section backgrounds had a fixed width in pixels, so on narrower screens they almost disappeared and on wider ones they overlapped the text by up to 30 pixels. I switched them to relative units so the visible part of the decoration stays the same regardless of window width.

Highlighted column contrast
1.07 → 1.32:1
Measured against the card background, before and after the new token.
Decoration overlapping text
30 → 0 px
Checked at window widths of 1280, 1440 and 1920 px.
The comparison table before the change: the Your meditation column has a background almost identical to the rest of the card
Contrast 1.07:1Green diluted to 40% opacity. The highlighted column barely stands out.
The comparison table today: the Your meditation column has a clear green background, rows have dash and tick markers
Contrast 1.32:1A separate color instead of a dilution. The row markers came in a follow-up fix the same day.

The same rule covered the voice. When I tried to speed up the pace with a playback parameter instead of in synthesis, the result sounded noticeably stretched, even though technically it was supposed to preserve pitch. I wrote it down as a rule in the code:

Pace belongs to synthesis, not to playback, even if playback seems cheaper.

I didn't trust a single measurement either. With unusual parameter values I measured several times, because one outlier almost made it into production as the final decision.

Bug

The meditation cut off on just one iPhone.

A tester reported that the recording ended after 20–33 seconds, even though the text and the credit limit pointed to a full session. The bug didn't show up on computers or on other phones.

The cause was anything but obvious. Every audio file from ElevenLabs carries an MP3 header declaring the length of the recording. I was joining audio segments byte by byte, so the finished file started with a header describing only the length of the first segment, usually exactly the 20–33 seconds of the opening sentence. Chrome ignores that header and calculates the length from the file size. Safari trusts it and stops playback exactly where the header says.

Chrome

Ignores the header, calculates the length from the file size and plays to the end. That's why there was no bug on any computer.

Safari

Trusts the header and stops playback where it says. On the iPhone, the meditation cut off after the first sentence.

Measured on a test join from the fixing session and recorded in the changelog: 615 frames in the header, i.e. 16.07 s, versus 46.00 s of recording. In the tester's report, the recording cut off after 20 and 33 seconds.

I wrote my own parser from scratch that strips these headers from every segment before joining. But something else taught me the most: I already had the same rule in my code. I had deliberately created the silence file inserted between segments without any metadata. I applied the rule to my own file, but not to the files coming in from outside.

After the fix, the tester saw the full 4:47 in the player instead of a few dozen seconds. And she immediately ran into the second layer of the same problem: where the segments met, the voice stalled, and the background music kept playing endlessly over silence.

Background music playing over silence is worse than an honest error message.

I added a stall detector that first tries to skip the faulty fragment inaudibly and, after a few failed attempts, stops playback with a message instead of pretending everything is still playing. After both fixes, I listened to a whole meditation on my own iPhone.

The TuMedytuj player: male voice selected, a play button, a progress bar at 0:00 of 4:30, below it background music options Space, Rain, Waves, No music and a background volume slider
My own player with background music. This is where the voice and the music drifted apart where the segments met.

Result

What's ready, and what I deliberately set aside.

The whole tool works: the user describes their situation, gets the meditation text, chooses a voice after hearing a sample, can add background music matched to the mood, and plays it all in my own player, consistent with the rest of the visual system.

The site also has a safety path. If what the user typed points to a crisis, I show a separate, calm screen with clickable support numbers instead of continuing with the meditation.

  1. Your own words instead of a list of categories

    Someone describes what they're dealing with today, and the tool picks a technique to match.

    The meditation card with the text I can't fall asleep and I feel empty typed in, the button in a loading state: Picking a technique matched to your situation
  2. Text written for that one situation

    The meditation responds to what was typed: the bed, the duvet, sleep. The vertical bars in the text are pauses you later hear in the recording.

    A finished meditation: You can let your eyelids close now, if you haven't already. It's enough that you're here, in bed, wrapped in your duvet. The Polish text covers both grammatical genders. Further breathing paragraphs are separated by vertical bars
  3. A voice with a sample, background music and the player

    I showed the voice choice with its disclosed limit in the audit, and the player in the iPhone bug.

Over the whole build and testing period, 1.83 hours of recordings were made for 35.6 thousand ElevenLabs credits. That's the project's main cost, because the text written by Claude cost pennies. Visit statistics showed 26 people, but only those who clicked consent. The rest, just as I designed it, left no trace.

Recordings
1.83 h
The whole build and testing period, my own tests included.
Voice cost
35.6k
ElevenLabs credits, dashboard from October 5, 2026.
Testers
~10
Family and friends. A number from memory, not measured.
Visitors
26
Only those who agreed to statistics.
Deliberately outside the MVP

I left user accounts and a real checkout outside the MVP, because they need separate payment infrastructure that I want to build only once I have real cost data from production, not assumptions.

For the same reason, search engine indexing is blocked for now: random traffic from Google shouldn't eat paid credits before the product makes money. These are deliberate decisions, documented with reasons, not oversights.

A small design system in Figma

For TuMedytuj I built a design system: variables, text styles, colors and components with variants. I made it in Figma and used AI to hook up the variables, styles, colors and text.

Token names and values match the site's code. The pricing on the Demo page is assembled from the same components, with the same packs and prices as on tumedytuj.pl.

See the file in Figma

For each section of the file I wrote down why I did it this way and not another. Three examples:

  • Color

    I started with the question of who comes here: someone in the evening, tired, who wants a moment of quiet. That's why the main color is a deep green, not blue, which feels like a bank or a work app.

  • Type

    I know a serif heading with sans-serif body text is now a layout associated with sites made by AI. I kept it because here it has a specific job.

  • Cards

    Only primary content gets a shadow, secondary content gets a border, and never both at once. If everything had a shadow, nothing would stand out.

The Demo page in Figma: the heading Start for free and three pricing cards, Free with a shadow, First Step with a border and Ritual on a dark background
DemoPricing assembled from component instances: Card, Badge and Button.
The Card component page with a Why this way note and three variants: Raised with a shadow and no border, Quiet with a border and no shadow, Dark on a dark background
CardThree variants with a note on why: a 28 px radius always, and a border and a shadow never on the same card.

Takeaway

What stays with me most is the method, not the app itself.

I wrote the code with the help of Claude Code. Beyond the interface itself, I put the most work into making sure the project didn't lose its memory. I updated changelog.md and design-system.md after every change, with the reason “why”, not just “what”. That let me come back to the project after a break, and decisions from a few days earlier didn't get lost in the next session. Keeping that documentation was as important a part of the work for me as the interface itself.

That's the method: measure instead of guessing, audit my own promises against the code, and keep documentation precise enough to work with AI step by step without losing the product's consistency.

I can repeat this method in my next project. The interface stays with this one.


Next case study

Booking

An independent concept: stays along your route in one place, in two variants and with a test plan.

Go to the project

Contact

Let's talk.

I'm looking for my first job as a UX/UI and Product Designer. I'm available right away.