Bardia Zadeh Résumé
Case study

FarsiChart, a bilingual music-data platform

I created, designed and built FarsiChart, an English and Persian music chart that now earns 71,107 Google clicks every 28 days, up from 8 in April 2026, and writes, designs and posts its own stories every day.

RoleCreator and sole owner; I designed and built it end to end and lead its design, data and search growth
TimelineStarted around 2020; current build is 2,079 commits since April 2026, still running
PlatformResponsive web in English and Persian (RTL), plus auto-posted Telegram and Instagram cards
ToolsClaude Code, PHP, Python, GitHub Actions, Cloudflare, Google Search Console, Vazirmatn and Inter type
FarsiChart, a bilingual music-data platform screenshot

The problem

FarsiChart is a daily music newsroom in English and Persian that runs with no editors, and it's entirely my own product. I created it around 2020, designed it and built it. It ranks Persian and Iranian artists by Spotify monthly listeners and RadioJavan plays, and readers come for the daily moves, like who passed whom or who broke out this week. Every one of those moves has to become a story in two languages and land on Telegram and Instagram the same day.

The catalogue tracks 27,000+ songs, and Google indexes 58,462 of the site's pages. Writing those stories by hand could never keep up.

The hard part is design. A native reader spots machine-made Persian in a second. Put a correct number in the wrong reading order and it reads as nonsense, and a chart that reads as nonsense loses the trust it runs on.

Who it's for

Persian readers bring 93% of FarsiChart's search clicks and click through 3.3x as often as English readers, so I design for them first. Most of what I know about them comes from their own searches and from 1,002 posted records I analysed.

They search in spoken Persian. An early #1 card was headed «صدرنشین چارت», roughly "chart leader". Nobody talks that way, so I cut it. Search data showed what they type instead, like «بهترین رپر ایران» ("best rapper in Iran"), and phrases like that became the headline on the #1 spotlight card.

Reading order covers numbers too, right to left. One draft line read, in effect, "plays this week 261,408". The number belongs on the far right, where it's read first, in Persian digits with the Persian thousands separator.

356 of the 382 artists have a Persian name on file, and that's the name readers know. The cards lead with it and fall back to Latin for the rest.

FarsiChart on a phone.
FarsiChart on a phone.

Key decisions

Each of these decisions started with something that looked wrong on the live product and ended as a rule the engine now enforces by itself.

A 3:4 frame with an English layer and a Persian layer

Every card now fills Instagram's grid cell edge to edge in both languages. The grid shows 3:4 cells (I measured 400 by 533 off FarsiChart's own profile), and it was center-cropping the old square cards straight through their text. "BAD ADAT" showed as "D ADAT". I moved the cards to 1080 by 1440. The wide English card sits in the middle, unchanged, and Persian bands above and below carry the headline and an event panel for the readers who bring most of the traffic.

Color sampled from the card itself

The compositor picks each band's color from the card it frames, and it matched 9 of 9 real cards. The first complaint was a green band on an orange card. The stored accent field said green for a recap drawn in cyan, and guessing from the category put gold on cyan. Every card already draws a thin accent rule near its bottom edge, and the compositor finds the most saturated row there and snaps it to the nearest brand color.

Every bottom band adds a fact

The Persian bands give readers a number they can't get from the English card alone. A Top 10 gets a podium with #1 in gold, and a breakout gets a bar of this week's plays against lifetime plays. The overtake band shows the real margin in listeners. If a stat doesn't exist, its chip disappears, because an early draft shipped an empty one. Genre words appear only for rap and pop, after the coarse traditional bucket once captioned a pop balladeer as traditional music.

Spotify and RadioJavan on every listen row

Every listen row now offers both platforms. It used to show only the platforms a record linked to. Across those 1,002 records, 53.8% had both links, but new releases were 55% Spotify-only because RadioJavan numbers reach FarsiChart on a slower cycle. A missing link usually meant the RadioJavan numbers hadn't arrived yet, and hiding the button left RadioJavan listeners with nothing to tap.

Only measured numbers reach a card

Every number on a card is a real measurement. Early on, a scraper fallback grabbed whatever number sat near the word "listener" and stored it as a real count, which shot a brand-new artist up the chart overnight. I deleted every fallback of that kind and added a plausibility gate that checks listeners against followers. When a pair fails, the system records zero and logs a warning instead of guessing. Replayed against every live artist, it rejected none of the real ones.

A 3:4 #1 card, with the English card in the middle and the Persian layers above and below. This render predates two fixes. It still carries «صدرنشین چارت» and a Latin comma in the Persian number.
A 3:4 #1 card, with the English card in the middle and the Persian layers above and below. This render predates two fixes. It still carries «صدرنشین چارت» and a Latin comma in the Persian number.

How I built it

I shipped the current build, 2,079 commits since April 2026, under standards I wrote. Nothing counts as done until I've checked it on the live system.

Scheduled jobs collect Spotify and RadioJavan numbers into a PHP API that stores them as JSON, and artist submissions wait in an admin dashboard until they're approved. The publishing engine reads each day's movement, picks the stories worth telling, posts them to Telegram and mirrors them to Instagram. It's published 1,300+ stories so far with no one writing them.

Every Persian word on a card comes from a pre-rendered Vazirmatn sprite. Persian broke first. The image library rendered «بیشترین رشد هفته» ("biggest gain of the week") as disconnected letters in reverse, and next to a proper text engine the fault was plain to see. The card vocabulary is small, so I reviewed every sprite by eye before it shipped.

I went past the documented platform limit. Meta's docs list 4:5 as the tallest feed size, so I staged an unpublished 1080 by 1440 post through the API, and it went through.

Mockups come from real records now. A hand-assembled one once paired one artist's card with another artist's name.

The approved renders came out with 0 out-of-bounds text and 0 overlaps. The spec makes every render pass a stress matrix that checks each text box against the frame and every pair of boxes for collisions, including hard cases like the longest name in the roster (24 characters, shrunk to fit) and an artist with no Persian name. The cyan and gold mismatch passed every one of those checks anyway. I caught it by looking, and I've read every render since.

What changed

Google now sends FarsiChart 71,107 clicks every 28 days, up from 8 in April 2026, off 2.7M search impressions. The July audit already showed about 20x growth, from 298 to 6,310 clicks per 28 days, and clicks more than doubled again in the latest 28-day window. The site is drawing 83K real visitors a month, with robots filtered out.

That growth comes from scale and language: 58,462 pages in Google's index, and 93% of clicks landing on the Persian side I designed first.

The engine does the job of a bilingual social desk. At 15 stories a day, that's 2,700+ hours a year of writing, card design and posting, worth $80K+, that no editor has to touch. The 3:4 cards are live now, after I signed off the spec on several rounds of renders built from real records.

2.7MGoogle search impressions every 28 days
83Kreal visitors a month, robots filtered out
71,107Google clicks every 28 days, up from 8 in April 2026
58,462FarsiChart pages in Google's index
27,000+songs tracked in the catalogue
2,700+hours a year of bilingual writing, card design and posting the engine does on its own

What's next

Next is 100K real visitors a month, up from 83K today. The route there runs through artist accounts, where artists claim their profiles and fill in their own facts while ranks and play counts stay locked, and through answer pages built for the AI assistants that already send a fast-growing share of readers. Every card band also gets scored on the saves and follows it earns, and any band that doesn't pull its weight gets cut.