
{"id":2131,"date":"2026-10-04T13:49:22","date_gmt":"2026-10-04T11:49:22","guid":{"rendered":"https:\/\/www.pcjv.de\/?p=2131"},"modified":"2026-10-04T13:49:22","modified_gmt":"2026-10-04T11:49:22","slug":"saftladen","status":"publish","type":"post","link":"https:\/\/www.pcjv.de\/?p=2131","title":{"rendered":"Saftladen: a weekend at the fruit stall"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/cwbudde.github.io\/Saftladen\/\" target=\"_blank\" rel=\"noopener\">Saftladen<\/a> is a Fruit Ninja-style game for the browser: fruit flies up, you slice it with the mouse or a finger, and you leave the bombs alone. The name is German. Literally it means &#8220;juice shop&#8221;. In everyday speech it is what you call an outfit where nothing works.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Play it<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The game runs at <a href=\"https:\/\/cwbudde.github.io\/Saftladen\/\" target=\"_blank\" rel=\"noopener\">cwbudde.github.io\/Saftladen<\/a> and works on a phone. After the first complete load it also works offline, installed as a web app.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1600\" height=\"1013\" src=\"\/wordpress\/wp-content\/uploads\/saftladen-menu.jpg\" alt=\"The Saftladen main menu: the wordmark with a sliced orange, three mode cards for Classic, Arcade and Zen, a short description of each mode and the next goal\" class=\"wp-image-2127\" srcset=\"https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-menu.jpg 1600w, https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-menu-300x190.jpg 300w, https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-menu-1024x648.jpg 1024w, https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-menu-768x486.jpg 768w, https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-menu-1536x972.jpg 1536w\" sizes=\"auto, (max-width: 1600px) 100vw, 1600px\" \/><figcaption class=\"wp-element-caption\">The menu. The wordmark is drawn in CSS and needs no image or web font.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">There are three modes:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Classic<\/strong> ends on a bomb or after three missed fruit.<\/li>\n\n\n\n<li><strong>Arcade<\/strong> gives you 60 seconds. A bomb costs half your score, and the pickups Freeze, Frenzy and Double Points help.<\/li>\n\n\n\n<li><strong>Zen<\/strong> is 90 seconds of fruit with no bombs and no strikes.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1600\" height=\"1024\" src=\"\/wordpress\/wp-content\/uploads\/saftladen-game5.jpg\" alt=\"An Arcade round in progress: a watermelon and an orange cut into halves with juice splashes, +10 score labels, a fading blade trail and a streak counter in the HUD\" class=\"wp-image-2128\" srcset=\"https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-game5.jpg 1600w, https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-game5-300x192.jpg 300w, https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-game5-1024x655.jpg 1024w, https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-game5-768x492.jpg 768w, https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-game5-1536x983.jpg 1536w\" sizes=\"auto, (max-width: 1600px) 100vw, 1600px\" \/><figcaption class=\"wp-element-caption\">Ten seconds into an Arcade round. The halves split along the direction of the cut, and the juice is the colour of the fruit.<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">How it is built<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">It is React, TypeScript and Vite, with Bun for the scripts. React only does the menus and dialogs. The game itself is plain Canvas2D, driven by <code>requestAnimationFrame<\/code>. Underneath runs a headless engine with a fixed 60 Hz step and six systems in a fixed order: mode clocks, spawning, physics, slice detection, slice resolution and despawning. The engine knows nothing about React or the canvas, so the tests can drive it directly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Gameplay randomness and cosmetic randomness come from separate seeded generators. A particle burst, or turning the effects off, can&#8217;t change which fruit comes next. With the same seed and the same inputs, a run plays out the same way.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What the weekend changed<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I started Saftladen on a weekend in February and left it playable but rough. This weekend I returned to it: 26 commits between Saturday 1 a.m. and Sunday noon. Swipes now hit what you see, throws follow a plan, the sound got richer, there are rewards to unlock, and the results screen is new. Underneath, it got onboarding, safe updates, smaller images and a lot of tests.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Cuts you can trust<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A fast swipe covers a lot of screen between two frames, and the fruit moves too. The old code tested each blade segment only against the fruit&#8217;s position after the physics step, so a slash through the apple you saw could miss it. Now every segment is tested against a capsule: the fruit&#8217;s circle swept across that step. Only fresh, moving segments count, so holding the mouse still on a fruit does nothing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Scoring now separates two ideas. A <em>stroke combo<\/em> counts fruit cut in one held gesture: three fruit earn 15 bonus points, each additional fruit 5 more. A <em>streak<\/em> chains cuts less than 320 ms apart and raises the multiplier by 0.25 every five hits, up to \u00d72.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Fair throws<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Spawning used to be random with a ramp. Now a spawn director plans it. Every mode opens with three safe single fruit, then six beats repeat: a fan, a staggered ladder, alternating side throws, two groups and a single fruit to recover. Fans and groups peak together, so they invite one horizontal cut.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Fruit stays in the middle corridor, bombs go straight up at the edges. Before a bomb is added, the director checks its whole flight against every fruit in the air or queued, and holds the bomb back if a cut through the group could catch it. 270 seeded runs across three screen shapes kept to these rules.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Sound from recipes<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The game ships no sound effect files. Each effect is a short recipe of layers: a sine or triangle sweep, seeded xorshift noise (bright or low-passed to warm) and an envelope. At startup the recipes are mixed once into 22 kHz WAVs, and Howler plays them. A cut has four layers and three variations, so rapid cuts don&#8217;t sound like a machine gun. The combo chord rises a semitone for every fruit beyond three.<\/p>\n\n\n\n<figure class=\"wp-block-audio\"><audio controls preload=\"metadata\" src=\"\/wordpress\/wp-content\/uploads\/saftladen-sfx.mp3\"><\/audio><figcaption class=\"wp-element-caption\">The effects in order: the three cut variations, combos of three, five and seven fruit, power-up on and off, and a bomb. Rendered at full scale, without the game&#8217;s mix.<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Something to play for<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Runs now earn XP and Starfruit, which unlock two more blades (blade trails) and two more dojos (backgrounds). There are six permanent achievements and three rotating challenges. The system is deliberately friendly: Starfruit is never spent, nothing expires, and there are no daily streaks to lose. Sunday morning went into the results screen, which is now a compact receipt from the fruit stall.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1600\" height=\"1024\" src=\"\/wordpress\/wp-content\/uploads\/saftladen-results.jpg\" alt=\"The Saftladen results dialog after an Arcade run: 1396 points, 163 fruit sliced, 118 misses, 5 bomb hits, best stroke combo 6 fruit, 52% stroke accuracy, a peak streak of 13 hits, and the XP and Starfruit earned\" class=\"wp-image-2129\" srcset=\"https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-results.jpg 1600w, https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-results-300x192.jpg 300w, https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-results-1024x655.jpg 1024w, https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-results-768x492.jpg 768w, https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/saftladen-results-1536x983.jpg 1536w\" sizes=\"auto, (max-width: 1600px) 100vw, 1600px\" \/><figcaption class=\"wp-element-caption\">The results screen. This run was played by a test script that swipes blindly back and forth across the screen, which explains the 118 misses.<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">The unglamorous part<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">New players get a skippable practice apple, and each run starts after a three-second countdown. A new version of the web app waits behind an <em>Update game<\/em> button instead of reloading mid-run. The game can be played entirely with the keyboard, and a setting turns off flashes and bursts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The 17 fruit sprites are now lossless WebP, 28.5% smaller with identical pixels. And behind it all are 188 Bun tests and 28 Playwright checks against the production build, five of them with real service workers for offline and update scenarios.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Trying it<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Playing only needs a browser: <a href=\"https:\/\/cwbudde.github.io\/Saftladen\/\" target=\"_blank\" rel=\"noopener\">cwbudde.github.io\/Saftladen<\/a>. To build it yourself:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git clone https:\/\/github.com\/CWBudde\/Saftladen\ncd Saftladen\nbun install --frozen-lockfile\nbun dev<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Press <code>D<\/code> in the game to show the debug overlay. It draws the blade trails, the bomb envelopes and the next spawn pattern.<\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>Saftladen is a Fruit Ninja-style game for the browser: fruit flies up, you slice it with the mouse or a finger, and you leave the bombs alone. The name is German. Literally it means &#8220;juice shop&#8221;. In everyday speech it is what you call an outfit where nothing works.<\/p>\n","protected":false},"author":0,"featured_media":2164,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[43],"tags":[],"class_list":["post-2131","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-typescript"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.6 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Saftladen: a weekend at the fruit stall - Christian&#039;s private site<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.pcjv.de\/?p=2131\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.pcjv.de\\\/?p=2131#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.pcjv.de\\\/?p=2131\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Saftladen: a weekend at the fruit stall\",\"datePublished\":\"2026-10-04T11:49:22+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.pcjv.de\\\/?p=2131\"},\"wordCount\":909,\"image\":{\"@id\":\"https:\\\/\\\/www.pcjv.de\\\/?p=2131#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.pcjv.de\\\/wordpress\\\/wp-content\\\/uploads\\\/teaser-saftladen.jpg\",\"articleSection\":[\"TypeScript\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.pcjv.de\\\/?p=2131\",\"url\":\"https:\\\/\\\/www.pcjv.de\\\/?p=2131\",\"name\":\"Saftladen: a weekend at the fruit stall - Christian&#039;s private site\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.pcjv.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.pcjv.de\\\/?p=2131#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.pcjv.de\\\/?p=2131#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.pcjv.de\\\/wordpress\\\/wp-content\\\/uploads\\\/teaser-saftladen.jpg\",\"datePublished\":\"2026-10-04T11:49:22+00:00\",\"author\":{\"@id\":\"\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.pcjv.de\\\/?p=2131#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.pcjv.de\\\/?p=2131\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.pcjv.de\\\/?p=2131#primaryimage\",\"url\":\"https:\\\/\\\/www.pcjv.de\\\/wordpress\\\/wp-content\\\/uploads\\\/teaser-saftladen.jpg\",\"contentUrl\":\"https:\\\/\\\/www.pcjv.de\\\/wordpress\\\/wp-content\\\/uploads\\\/teaser-saftladen.jpg\",\"width\":480,\"height\":360},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.pcjv.de\\\/?p=2131#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.pcjv.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Saftladen: a weekend at the fruit stall\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.pcjv.de\\\/#website\",\"url\":\"https:\\\/\\\/www.pcjv.de\\\/\",\"name\":\"Christian&#039;s private site\",\"description\":\"Private site of Christian-W. Budde\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.pcjv.de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Saftladen: a weekend at the fruit stall - Christian&#039;s private site","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.pcjv.de\/?p=2131","twitter_misc":{"Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.pcjv.de\/?p=2131#article","isPartOf":{"@id":"https:\/\/www.pcjv.de\/?p=2131"},"author":{"name":"","@id":""},"headline":"Saftladen: a weekend at the fruit stall","datePublished":"2026-10-04T11:49:22+00:00","mainEntityOfPage":{"@id":"https:\/\/www.pcjv.de\/?p=2131"},"wordCount":909,"image":{"@id":"https:\/\/www.pcjv.de\/?p=2131#primaryimage"},"thumbnailUrl":"https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/teaser-saftladen.jpg","articleSection":["TypeScript"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.pcjv.de\/?p=2131","url":"https:\/\/www.pcjv.de\/?p=2131","name":"Saftladen: a weekend at the fruit stall - Christian&#039;s private site","isPartOf":{"@id":"https:\/\/www.pcjv.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.pcjv.de\/?p=2131#primaryimage"},"image":{"@id":"https:\/\/www.pcjv.de\/?p=2131#primaryimage"},"thumbnailUrl":"https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/teaser-saftladen.jpg","datePublished":"2026-10-04T11:49:22+00:00","author":{"@id":""},"breadcrumb":{"@id":"https:\/\/www.pcjv.de\/?p=2131#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.pcjv.de\/?p=2131"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.pcjv.de\/?p=2131#primaryimage","url":"https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/teaser-saftladen.jpg","contentUrl":"https:\/\/www.pcjv.de\/wordpress\/wp-content\/uploads\/teaser-saftladen.jpg","width":480,"height":360},{"@type":"BreadcrumbList","@id":"https:\/\/www.pcjv.de\/?p=2131#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.pcjv.de\/"},{"@type":"ListItem","position":2,"name":"Saftladen: a weekend at the fruit stall"}]},{"@type":"WebSite","@id":"https:\/\/www.pcjv.de\/#website","url":"https:\/\/www.pcjv.de\/","name":"Christian&#039;s private site","description":"Private site of Christian-W. Budde","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.pcjv.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"}]}},"_links":{"self":[{"href":"https:\/\/www.pcjv.de\/index.php?rest_route=\/wp\/v2\/posts\/2131","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pcjv.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pcjv.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pcjv.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2131"}],"version-history":[{"count":0,"href":"https:\/\/www.pcjv.de\/index.php?rest_route=\/wp\/v2\/posts\/2131\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.pcjv.de\/index.php?rest_route=\/wp\/v2\/media\/2164"}],"wp:attachment":[{"href":"https:\/\/www.pcjv.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2131"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pcjv.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2131"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pcjv.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2131"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}