{"id":50384,"date":"2025-12-10T16:11:07","date_gmt":"2025-12-10T21:11:07","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=50384"},"modified":"2025-12-11T13:25:41","modified_gmt":"2025-12-11T18:25:41","slug":"how-to-make-a-macos-screen-saver","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2025\/12\/10\/how-to-make-a-macos-screen-saver\/","title":{"rendered":"How to Make a macOS Screen Saver"},"content":{"rendered":"<p><a href=\"https:\/\/wadetregaskis.com\/how-to-make-a-macos-screen-saver\/\">Wade Tregaskis<\/a>:<\/p>\n<blockquote cite=\"https:\/\/wadetregaskis.com\/how-to-make-a-macos-screen-saver\/\">\n<p>Given how buggy Apple&rsquo;s screen saver framework is, I suggest <em>not<\/em> relying on <code>animateOneFrame<\/code> if you can at all avoid it.  Even if that means setting up your own timer.  That way when they likely break that too in some future macOS release, your screen saver won&rsquo;t necessarily break as well.<\/p>\n<p>[&#8230;]<\/p>\n<p><code>stopAnimation<\/code> is <em>only<\/em> used for the live preview thumbnail shown in the Screen Saver System Settings pane.  It is <em>never<\/em> called in normal operation of the screen saver (contrary to what Apple&rsquo;s documentation says &#x2013; Apple broke that in macOS Sonoma and later).<\/p>\n<p>[&#8230;]<\/p>\n<p>Here&rsquo;s the second big bug in Apple&rsquo;s screen saver framework &#x2013; every time the screen saver starts, your <code>ScreenSaverView<\/code> subclass is created again.  But the old one doesn&rsquo;t go anywhere.  So now you have <em>two<\/em> copies running simultaneously, which as at the very least wasteful, and can easily lead to gnarly bugs and weird behaviour (e.g. if both are playing sound, or both modify persistent state).<\/p>\n<p>[&#8230;]<\/p>\n<p>Unfortunately Apple&rsquo;s screen saver system will never terminate your screen saver process.  Worse, even if you do <em>nothing<\/em> yourself, Apple&rsquo;s screen saver framework code will run in an infinite loop, wasting [a small amount of] CPU time.<\/p>\n<\/blockquote>\n<p>There was a longstanding API that worked fine for many years but then got progressively more broken.<\/p>\n\n<p id=\"how-to-make-a-macos-screen-saver-update-2025-12-11\">Update (<a href=\"#how-to-make-a-macos-screen-saver-update-2025-12-11\">2025-12-11<\/a>): <a href=\"https:\/\/mastodon.social\/@jamesmillerio\/115699089445713544\">James Miller<\/a>:<\/p>\n<blockquote cite=\"https:\/\/mastodon.social\/@jamesmillerio\/115699089445713544\">\n<p>Same experience years ago making a recreation of the After Dark Flying Toaster screensaver. It was such a strange development process&#8230;<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/phpc.social\/@kboyd\/115699224772912747\">Kevin Boyd<\/a>:<\/p>\n<blockquote cite=\"https:\/\/phpc.social\/@kboyd\/115699224772912747\">\n<p>I tried writing one a few years ago and got stymied by everything being terrible &amp; no information being available. Maybe I can get back into it soon.<\/p>\n<\/blockquote>","protected":false},"excerpt":{"rendered":"<p>Wade Tregaskis: Given how buggy Apple&rsquo;s screen saver framework is, I suggest not relying on animateOneFrame if you can at all avoid it. Even if that means setting up your own timer. That way when they likely break that too in some future macOS release, your screen saver won&rsquo;t necessarily break as well. [&#8230;] stopAnimation [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"2025-12-10T21:11:11Z","apple_news_api_id":"5e9acb0b-f6d9-44ce-acbf-8dc345c8eddf","apple_news_api_modified_at":"2025-12-11T18:25:45Z","apple_news_api_revision":"AAAAAAAAAAAAAAAAAAAAAA==","apple_news_api_share_url":"https:\/\/apple.news\/AXprLC_bZRM6sv43DRcjt3w","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[4],"tags":[131,689,30,2742,71,1917,901],"class_list":["post-50384","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-bug","tag-core-animation","tag-mac","tag-macos-tahoe-26","tag-programming","tag-screensaver","tag-swift-programming-language"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/50384","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=50384"}],"version-history":[{"count":2,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/50384\/revisions"}],"predecessor-version":[{"id":50392,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/50384\/revisions\/50392"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=50384"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=50384"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=50384"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}