Koshin Studio
A one-person studio. I build browser games and small apps, and write down what I measure.
Free. No install, no sign-up.
Play
- Sushi Stack — A falling-block puzzle: collide matching sushi toppings to grow them, all the way up to the golden sushi tub. Available in 14 languages, opens in your browser language. (About, English)
- Negaeri Shogi — A 6×6 board game. Pieces you sandwich defect to your side and get promoted, and chains flip many at once. Local, vs AI, and online with ratings. The game is in English and Japanese. (rules, English)
- Stop at 10 seconds — Count ten seconds in your head and stop. Best of three; the smaller the error, the stronger you are. No setup, no sign-up. The interface is in Japanese. (About, English)
- Anime Type Quiz — 12 questions, 6 axes: your anime type and the one anime that fits you right now. Free, nothing is sent anywhere. In English.
- トレカダッシュ (Toreca Dash) — Lottery, pre-order and restock alerts for trading cards sold in Japan.
Tools
- Gacha completion calculator — How many pulls, and how much money, to collect every type?
- Gacha drop rate and pity calculator — From the drop rate, how many pulls until you get it?
- Lottery win chance calculator — From the odds and the number of entries, the chance you win at least once.
Development logs
Measured results and fixes, written up with numbers. These articles are in English and Japanese.
- Measuring the gap between ads in time, not in plays
- The conger eel was too big: finding why a merge puzzle’s last piece was out of reach
- From 16,177 titles to 6,091: how the anime quiz database was built by dropping over 60%
- Firebase Auth: force-quitting the app created anonymous accounts in bulk (waiting for currentUser to restore)
- Japanese text garbled in the CrazyGames preview: the file was correct, the cause was never found, so I escaped to ASCII
- What I read as “fetched” was just a notice page: three days of zero products
- A bulk update hit “all articles”: count before you write, and break your tests to prove they work
- Pokémon card sleeves and loaders: sizes and what “pt” means
- Claude API vs Gemini API by official numbers: small-model prices, web search, spend limits and deprecation
- Comparing comment systems for static sites by their official information: giscus, utterances, Disqus, Remark42 and more
- Rejected by CrazyGames with no reason given: I recorded the game and found four problems
- Works on my machine, breaks when deployed: four incidents shipping a static site to Cloudflare Pages
- I counted “duplicates are practically zero,” but the audit reported 112 pairs
- It let things through when it could not check: how “fail-open” brought in resale items
- Splitting a “9-pack set” by contents created 80 fake products
- Cutting six buttons to one: first-time players did not know what to press
- Migrating from the Claude API to the Gemini API (gemini-3.1-flash-lite): 16 call sites into one function
- Comparing static site hosts by their official terms: Cloudflare Pages, Netlify, Vercel, GitHub Pages and Firebase Hosting
- Comparing iOS ad SDKs and Firebase by their official information: AdMob, AppLovin MAX, LevelPlay and Supabase
- ITMS-91064 “Invalid Binary”: NSPrivacyTracking was true but NSPrivacyTrackingDomains was empty
- A canvas becomes twice as large on Retina: inset:0 does not stretch it (a bug in a logo that bursts when you tap it)
- “80% was chosen over 82%”: the quiz’s match percentage disagreed with its ranking
- Measuring Qiita’s “posting too frequently” limit: a new account could not create another article for about 30 minutes
- Making a ranking server use only half of the free tier: from 5 calls to 2
- The streaming buttons were shown even for titles you could not watch: making the quiz result screen true
- App Store review sent “Guideline 2.1 Information Needed”: the six things Apple asked for
- I re-measured a “nothing can be done” assumption and scraping cost dropped to one eighth
- What changes when an anime quiz goes from 50 questions to 12?
- Making a 10-second stopwatch ranking fair: 3 decimals, ties and write counts
- “The pile settles at 89%” was wrong: my measurement was what was wrong
- The “9-pack set” was nine different products: the same product that never matched
- “It should shrink” was wrong twice: cutting AI calls and what I measured afterward
- No builds arriving in TestFlight: I had used up the 25 free Xcode Cloud hours (my first diagnosis was wrong)
- Four things that stopped my iOS app before App Review: iPad screenshots, ITMS-91064, account deletion, reporting posts
- iOS app freezes and is killed (0x8BADF00D): a SwiftUI computed property ran on every redraw
- A typo born once multiplied across shops: the “30th SEREBRATION” incident
- Python splitlines() breaks JSONL: the culprit was U+2028, an invisible line separator
- I could not listen, so I measured the waveform: building Japanese instruments with WebAudio for Sushi Stack
- Firebase 12 does not work with Xcode 15.2 (Swift 6.0 required): why I pinned Firebase 11.4.0 and AdMob 11.5.0
- Firestore cost in yen: 91% of reads came from one place, and my first cache did not work
- Before cutting Gemini cost, I doubted the meter: price, breakdown and cache were all wrong, and the cache threshold changed when I re-measured
- Setting the rules of Negaeri Shogi by playing AI against AI: one change that worked, six that broke
- A “1-minute” monitor checked each item once per 10 minutes: before speeding up, count the revisit interval and what was delivered
- Four real lotteries silently thrown away: a regex, dates without a year, batched answers and null
- “Checked” marks on articles that had never been read: the t.co relay page
- Notifications cannot be recalled: 19% of the last 72 hours’ notifications were wrong, and the gate worked after the fact
- Every deploy re-ran the daily jobs: 477 runs in 48 hours, 46.6% of that day’s Gemini cost
- Four times my own tools hurt me: a summary dropped conditions, one undefined stopped saving, a check locked out production, alerts that never cleared
- Before widening concurrency slots: widening a 2-core server from 8 to 14, and the earlier conclusion that was half wrong
- “The same application page” does not mean “the same store”: 52 articles showed another store’s name
- Three mix-ups in splitting one article into products: a child treated as a parent, a shared link pointing to another store, a name owned by two products
- When first-come sales start, the site becomes a waiting-room page: a 15 KB notice slipped through an 8,000-byte gate
- 393 “work-name-only” products from an official list: the name field did not contain the product form
- Fixing a gate does not fix existing data: a “tried already” mark, a URL marker and a lookup table
- Fixing product genre from the official catalogue: three dry runs took 13 changes down to 4
- When you cannot watch everything quickly: popular 128 of 427 sets run fast, and the fastest lane yields to the budget
- Online play that trusts nothing the client says: the server decides moves, clocks and results
- Four things from a full sweep of 61 audits: 51 jobs clumped at 4 a.m., a lock that expired in 55 seconds, and a price-filling idea I decided not to add
- Comparing with other sites to count gaps: the first “23% coverage” was a measuring mistake and the truth was 49%
- I opened the links of displayed articles and compared them: about 60 mistakes, and three mistakes in my own first check
- Four gaps in monitoring X: posts missing from search, accounts that change hands, a store-finding query, and catching up after an outage
- “It gets cleaned up later” hid an entrance bug: 400 near-identical products were re-created every day
- A secret token was in the logs in plain text: scrub at the one exit that cannot be forgotten
- Moving from Cloud Functions to a ¥4,300 server: ¥3,700 a day, Yahoo! failing in the EEA, and five traps
- Checks that logged “deferred to next run” had never run: three time budgets counted from the same start
- “That’s just a Beyblade” came back after I added a gate: the blocked value leaked in another form
- With no ads, 25 real-device checks were blocked: test ads in developer mode only, and no hard-coded prize names
- I fixed a timeout three times and it did not stop: the culprit was a second process under the same lock
- Looking up an ambiguous store name returns Shizuoka, the middle of Japan: I judged by Google’s own answer, not a list of chains
- A function named “load from iCloud if newer” compared nothing: a 20-record history could become 1
- If you throw failure away, the screen lies: a calendar that said “registered” with none added, a frozen OCR, a notification without a store name
- Candidates were found, yet reading the answer failed every time: a URL cut at 200 tokens, and a search that returned 400 on hyphens
- “One page = one case” was false: articles of a shop taking 6 products on one form were hidden just for sharing a link
- “ヤマダ電機” versus “ヤマダデンキ”: spelling variants and duplicates had three overlapping causes
- English and katakana names share no characters: a fingerprint gate rejected 42 correct matches
- “Automate everything that can be”: counting showed 66 repair jobs had never run on a schedule, and I added only six
- “The server went down and I cannot recover it” was wrong: only inbound SSH had been blocked
- “Weird articles keep multiplying”: I counted by creation route and closed the entrances, and my counting was wrong twice
- An aggregator crawl read only page 1: card articles were on pages 2–4, and only the first 6,000 characters went to the AI
- “Not just noticing; not letting it happen”: turning a report-only monitor into one that fills gaps on the spot
- The same report for the third time: a SwiftUI button responded only on its text, and my two earlier “fixes” did nothing