<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Copper • A blog about conductive layers</title>
    <link>https://alinpanaitiu.com/</link>
    <description>A place for long journeys into reverse engineering random things, scratching annoying itches with code and automating whatever can be automated.</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <managingEditor>alin@panaitiu.com (Alin Panaitiu)</managingEditor>
    <webMaster>alin@panaitiu.com (Alin Panaitiu)</webMaster>
    <copyright>This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</copyright>
    <lastBuildDate>Mon, 18 Feb 2019 12:27:33 -0600</lastBuildDate>
    
        <atom:link href="https://alinpanaitiu.com/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>How good is Claude, really?</title>
      <link>https://alinpanaitiu.com/blog/how-good-is-claude-really/</link>
      <pubDate>Wed, 04 Mar 2026 13:56:13 +0200</pubDate>
      <author>alin@panaitiu.com (Alin Panaitiu)</author>
      <guid>https://alinpanaitiu.com/blog/how-good-is-claude-really/</guid>
      <description>
        <![CDATA[<p>At the beginning of 2026, I didn&rsquo;t even know what vibe coding meant.</p>
<p>So much has happened in the time I was busy carving wooden spoons, that I felt it simpler to just ignore the hype.</p>
<p>It will pass, just like the NFTs and the dApps and microservices, I don&rsquo;t need to care about it. Or that&rsquo;s how I thought.</p>














<a href="https://alinpanaitiu.comimages/seasoning-claude.png">
  <figure>
    
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 800px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/seasoning-claude.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/seasoning-claude.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/seasoning-claude.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/seasoning-claude.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/seasoning-claude.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/seasoning-claude.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/seasoning-claude.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/seasoning-claude.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/seasoning-claude.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/seasoning-claude.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/seasoning-claude.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/seasoning-claude.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/seasoning-claude.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 800px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/seasoning-claude.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/seasoning-claude.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/seasoning-claude.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/seasoning-claude.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/seasoning-claude.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/seasoning-claude.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/seasoning-claude.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/seasoning-claude.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/seasoning-claude.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/seasoning-claude.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/seasoning-claude.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/seasoning-claude.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/seasoning-claude.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 800px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/seasoning-claude.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/seasoning-claude.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/seasoning-claude.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/seasoning-claude.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/seasoning-claude.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/seasoning-claude.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/seasoning-claude.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/seasoning-claude.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/seasoning-claude.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/seasoning-claude.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/seasoning-claude.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/seasoning-claude.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/seasoning-claude.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/seasoning-claude.png"
        
        
        
        style="max-width: min(800px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>One morning, in this seemingly never ending winter, a friend told me excitedly how his employer got them Claude subscriptions and encouraged them to try it for work. And his reaction was something along the lines: <em>&ldquo;it&rsquo;s crazy! I love it! we&rsquo;re doomed&hellip;&rdquo;</em></p>


  <figure style="width: min(800px, 80vw); margin: auto; position: relative">
    <video defer autoplay loop muted playsinline disablepictureinpicture style="width: 100%; margin: 0; border-radius: 12px; box-shadow: none" id="winter-video" >
      <source src="/video/neverending-winter-2026.mp4">
  </video>
  <div class="inner-shadow" onclick="var v = document.getElementById('winter-video'); if (!v.paused) { v.pause(); } else { v.play(); }" ></div>
  <style>
    .inner-shadow {
        position: absolute;
        width: 100%;
        height: 100%;
        box-shadow: inset 0px 0px 10px 3px rgba(1,0,5,0.35);
        top: 0;
        left: 0;
        border-radius: 12px;
    }
  </style>
  </figure>

<p>I felt that was an overreaction, I tried LLMs for coding before and aside from small snippets, it couldn&rsquo;t do more than 50 lines without stumbling on itself.</p>
<p>But it was a really gray hazy winter morning, and I didn&rsquo;t feel like splitting wood in the quiet of the house, so I made some coffee and got my laptop out.</p>
<h2 id="stages">Stages</h2>
<p>I gave it the usual task that I really want to see done but I&rsquo;m always dreading to start: <strong>Stages in <a href="https://lowtechguys.com/rcmd"  target="_blank" rel="noopener" >rcmd</a></strong></p>
<blockquote>
<p>rcmd is a macOS app switcher that allows you to hold <code>Right Command</code>, and press the first letter of the app name. So press <code>V</code> for VSCode, <code>G</code> for Ghostty, <code>C</code> for Chrome: instant app switching without cycling through app icons.</p>
<p>Stages was dreamed up as a way to switch <em>workspaces</em> with the same paradigm. So press <code>C</code> for Coding where VSCode and Ghostty would focus and everything else was hidden, <code>D</code> for Design and so on.</p>
<p>But the functionality is way harder than it seems, the app needs to <em>record</em> the on-screen windows, and be able to <em>restore</em> them when the computer is restarted.</p>
</blockquote>
<p>The task was complex, just to outline some things Claude needed to do:</p>
<ul>
<li>understand the pre-existing app codebase with all its UI quirks</li>
<li>refactor the UI to be able to present <em>sets</em> of apps and windows</li>
<li>find ways to record custom window data like:
<ul>
<li>the project opened in VSCode</li>
<li>the working directory in Ghostty</li>
<li>the current URL in Chrome</li>
<li>files/folders/projects opened in other apps like Finder, Pixelmator, Sketch, Xcode etc.</li>
</ul>
</li>
<li>find ways to re-open windows at that specific project/file/folder</li>
<li>figure out a way to <em>hide</em> individual windows on macOS</li>
</ul>
<blockquote>
<p>It&rsquo;s weird, but there&rsquo;s no instant hide/show for windows on macOS. You can <em>minimize</em> them, but that has slow animations and unwanted side effects.</p>
<p>I mentioned to Claude that I knew a <a href="https://github.com/nikitabobko/AeroSpace"  target="_blank" rel="noopener" >window manager called Aerospace</a> which had a workaround for this.</p>
</blockquote>
<p>I gave Claude a 200 line <code>stages.md</code>, let it loose on the rcmd code, and stared at it crunching stuff for a few minutes. I went to make another coffee, and when I came back it was&hellip; done?</p>
<p>I hit <em>Build</em> in Xcode and was prepared to see a barrage of errors, that&rsquo;s how it always was. I got ready to screenshot the errors and the stupid code diff and show my friend how <em>&ldquo;no, we&rsquo;re not doomed, it just got lucky on that specific task&rdquo;</em>.</p>
<p>No errors though, the app compiled and launched like nothing happened. Uhh.. now what? The code diff was immense, I guess I should just see if anything actually works anymore.</p>
<p>I hid every app except VSCode and Ghostty, pressed <code>rcmd + lalt - C</code> and it said <strong>Stage C was assigned</strong>. No way this works&hellip; I brought back all apps on screen, quit VSCode, pressed <code>rcmd - C</code> and instantly VSCode and Ghostty were the only apps visible and focused, in the correct folder and project.</p>
<p>I looked through its thinking process and it even did a web search for <em>Aerospace macOS window manager</em>, found its Github, reverse engineered its workings until it found the <em>window hiding workaround</em> I mentioned in passing and implemented it from scratch in rcmd.</p>
<p>Astonished is a small word for what I felt.</p>
<p>Of course, this was not a feature ready for release. There was so much code review and so much polishing work to do <em>(not all apps have ways to restore their state after quitting, some focus flickering happened, and Stage creation should be more intuitive)</em>.</p>
<p>But the fact that an LLM could do such complex work, changed everything.</p>
<h2 id="picture-in-picture">Picture-in-picture</h2>
<p><strong><a href="https://lowtechguys.com/pipiri"  target="_blank" rel="noopener" >🔗 Pipiri</a></strong> <em>is a macOS app that can show any window in a floating picture-in-picture window</em></p>
<p>I had this barebones app I did as a standalone Swift file, for creating a <strong>Picture-in-Picture view of a window</strong> where there&rsquo;s data I&rsquo;d like to keep an eye on.</p>














<a href="https://alinpanaitiu.comimages/pipiri-ui.png">
  <figure>
    <figcaption>A Picture-in-Picture view of a Claude session floating over Xcode</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 1000px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/pipiri-ui.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/pipiri-ui.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/pipiri-ui.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/pipiri-ui.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/pipiri-ui.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/pipiri-ui.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/pipiri-ui.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/pipiri-ui.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/pipiri-ui.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/pipiri-ui.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/pipiri-ui.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/pipiri-ui.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/pipiri-ui.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 1000px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/pipiri-ui.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/pipiri-ui.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/pipiri-ui.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/pipiri-ui.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/pipiri-ui.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/pipiri-ui.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/pipiri-ui.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/pipiri-ui.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/pipiri-ui.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/pipiri-ui.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/pipiri-ui.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/pipiri-ui.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/pipiri-ui.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 1000px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/pipiri-ui.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/pipiri-ui.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/pipiri-ui.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/pipiri-ui.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/pipiri-ui.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/pipiri-ui.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/pipiri-ui.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/pipiri-ui.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/pipiri-ui.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/pipiri-ui.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/pipiri-ui.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/pipiri-ui.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/pipiri-ui.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/pipiri-ui.png"
        alt="A Picture-in-Picture view of a Claude session floating over Xcode"
        
        
        style="max-width: min(1000px, 90vw)"
      >
    </picture>
  </figure>
</a>

<p>I mostly used it to <em>watch long-running terminal commands</em> while I continued coding, and for <em>keeping Xcode logs visible</em> while I tested parts of my app.</p>
<p>I thought it was too niche to release, so I didn&rsquo;t have the motivation to do the work on creating an UI, licensing, webpage, icon etc.<br>
I saw this as an opportunity to see if Claude can do it. After all, I still felt this to be a useful idea after years of using it.</p>
<p>I created a blank Xcode macOS app project and gave Claude minimal instructions:</p>
<ul>
<li>adapt <code>pip.swift</code> to a <em>menubar app</em> with a SwiftUI architecture</li>
<li>add a <em>Settings window</em> where I can adjust the trigger hotkey, the position of the PiP panel etc.</li>
<li>implement <em>auto-updates</em> with Sparkle</li>
<li>add <em>license verification</em> through Gumroad</li>
<li>link to a <em>Contact form</em> and a privacy policy, similar to how I do it in my other apps</li>
</ul>
<p>What can I say? It did everything. Granted, the main PiP functionality had already been written manually by me years ago, this was just doing the things that every macOS dev hates to do but has to repeat on every app.</p>
<p>Then onto <a href="https://lowtechguys.com/pipiri"  target="_blank" rel="noopener" >the webpage</a>. Manually wrote a list of features with explanations in a <code>README.md</code> because I prefer to address future users of my apps personally, then told it to build the landing page by taking inspiration from my other app pages, using what I wrote in the README.</p>
<p>I was more than relieved to see 95% of the webpage look exactly as I would have done it myself. Not a single word changed, only HTML structure, colors, image paths etc. Adapting old webpage structure for a new app was not something I enjoyed doing.</p>
<blockquote>
<p>I also got a little carried away and asked Claude to implement a VM bytecode interpreter and write the offline license validation in that bytecode. I didn&rsquo;t expect it to actually do it! It even added a license generator CLI on top.</p>
</blockquote>
<p>I launched a <code>v1.0</code> and it was received well enough. Got 2 feature requests from the first day:</p>
<ul>
<li>Multi-window mode <em>(allow keeping multiple PiP panels visible from different)</em></li>
<li>Idle/Change detection <em>(get notified when the window contents haven&rsquo;t changed in a while, or if they did change after being still for a while)</em></li>
</ul>
<p>These are features I would have liked myself, but was too busy to start working on them before.</p>
<p>Now, Claude was not able to do these properly by simply mentioning the features. I had to explain the features in detail, how I want the multi-window PiPs to appear on top of each other, what APIs it should use, describe how the idle notification should look and that it&rsquo;s not a system notification.</p>
<p>In the end, the code needed a lot of review, testing and manual fixing, but just getting me started on the features was enough to motivate me to finish and release them.</p>
<p>Getting a semi-functional feature in readable code that I can polish is much more motivating than getting a huge refactor and full implementation that doesn&rsquo;t even compile.</p>
<hr>
<p>After releasing the <em>change detection</em> feature, I got a nice message from someone working at a hospital telling me how they use it to know when a patient has been added to the admission list without having to check it very second.</p>
<p>This is always humbling to me when it happens, reminds me how naive and limited we are as devs thinking that we considered all use cases. When in fact we should probably just build the tools, and people will find use for them in their own way.</p>
<h2 id="event-based-automation">Event-based automation</h2>
<p><strong><a href="https://lowtechguys.com/crank"  target="_blank" rel="noopener" >🔗 Crank</a></strong> <em>is a macOS app that can run scripts and Shortcuts on event triggers</em></p>
<p>My little brother was telling me about how he&rsquo;s trying to find ways to make money without having to work long hours in coffeeshops and restaurants.<br>
These AI tools are an opportunity for people like him, who know what they&rsquo;d like to be doing, but can&rsquo;t because they have to sell their time for a meager salary.</p>
<p>He has already started a few ideas like websites for small local shops that want an online presence, copywriting, product photo shoots.</p>














<a href="https://alinpanaitiu.comimages/website-photoshoot-honey.jpeg">
  <figure>
    <figcaption>can you believe those product shots are generated from a bland photo of a jar of honey?</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 1000px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 1000px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 1000px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/website-photoshoot-honey.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/website-photoshoot-honey.jpeg"
        alt="can you believe those product shots are generated from a bland photo of a jar of honey?"
        
        
        style="max-width: min(1000px, 90vw)"
      >
    </picture>
  </figure>
</a>

<p>So I thought why not also throw in a simple macOS app. I can bring in the ideas that I&rsquo;ve been sitting on for the past 5 years, help with code review and fixing, and he can just prompt Claude and test the app until it&rsquo;s working as expected.</p>
<p>The main theme was <em>simple, low maintenance apps only</em>.</p>
<hr>
<p>I had this idea for an <strong>event-based automation app</strong>: just the basic parts of what has already been done in Keyboard Maestro or Shortery, at a much cheaper price and free for basic needs.</p>














<a href="https://alinpanaitiu.comimages/crank-ui.png">
  <figure>
    <figcaption>The UI of the Crank macOS app</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 1000px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/crank-ui.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/crank-ui.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/crank-ui.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/crank-ui.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/crank-ui.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/crank-ui.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/crank-ui.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/crank-ui.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/crank-ui.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/crank-ui.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/crank-ui.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/crank-ui.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/crank-ui.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 1000px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/crank-ui.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/crank-ui.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/crank-ui.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/crank-ui.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/crank-ui.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/crank-ui.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/crank-ui.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/crank-ui.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/crank-ui.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/crank-ui.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/crank-ui.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/crank-ui.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/crank-ui.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 1000px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/crank-ui.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/crank-ui.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/crank-ui.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/crank-ui.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/crank-ui.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/crank-ui.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/crank-ui.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/crank-ui.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/crank-ui.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/crank-ui.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/crank-ui.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/crank-ui.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/crank-ui.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/crank-ui.png"
        alt="The UI of the Crank macOS app"
        
        
        style="max-width: min(1000px, 90vw)"
      >
    </picture>
  </figure>
</a>

<p>In support emails and chats, I&rsquo;ve had a lot of people ask for some very niche functionality in my other apps. Usually, it&rsquo;s some kind of automation, things like:</p>
<ul>
<li><em>&ldquo;can I disable the MacBook screen only when I connect my Dell monitor at home?&rdquo;</em></li>
<li><em>&ldquo;how can I make all screens black when I want to audition a piece in my recording studio?&rdquo;</em></li>
<li><em>&ldquo;can optimised images in <code>~/Blog</code> be renamed to <code>YYYY-MM-dd-name</code> and converted to <code>webp</code>?&rdquo;</em></li>
<li><em>&ldquo;please add an option to toggle HDR when I open IINA or any other movie app&rdquo;</em></li>
</ul>
<p>So I wanted to give those people the ability to resolve their frustrations without having to wait for a dev to do it.</p>
<p>I hopped on a screen sharing session with my brother and let him ask Claude to build this app bit by bit. The first thing that jumped out is: <strong>you have to know the terminology</strong> to get quality results.</p>
<p>He&rsquo;s not a dev, he doesn&rsquo;t know what a <code>debounce</code> or <code>throttle</code> is, so incidentally, he doesn&rsquo;t know to ask Claude to implement it for events that arrive in bursts. He has no idea what a <em>private API</em> is so he can&rsquo;t guide Claude to use one where we need to access low-level system APIs. And obviously concepts like <em>dry run</em> or <em>event log</em> are not familiar, but definitely useful in an app like this.</p>
<blockquote>
<p>Interestingly though, he <strong>was</strong> able to get Gemini to write a prompt for Claude that included a lot of these features that you&rsquo;d find in an automation app. I wouldn&rsquo;t have thought of that.</p>
<p>But dev intervention was still necessary. Some added features were just dangling in the UI without any effect internally, and it was impossible for a non-dev to know what they were for.</p>
</blockquote>
<p>We started that way, then he continued on his own while I worked on my own apps. Even without my assistance, the result was still a very functional app that could run scripts or launch apps automatically on event triggers.</p>
<p>And the thing is, people could have used this as is, it was useful enough to be released. But I knew it wouldn&rsquo;t be received well, people expect a certain polish for apps that have paid tiers.</p>
<h3 id="the-dev-touch">The dev touch</h3>
<p>To be really useful, the app needed some triggers that Claude did not know to implement on its own because such code uses obscure private APIs and IOKit data. Things like:</p>
<ul>
<li><strong>ambient light</strong> data through the MacBook&rsquo;s integrated sensor <em>(<a href="https://github.com/alin23/mac-utils/blob/main/ALS.swift"  target="_blank" rel="noopener" >ALS.swift</a>)</em></li>
<li>if <strong>camera</strong> is turned on or off <em>(<a href="https://github.com/alin23/mac-utils/blob/main/IsCameraOn.swift"  target="_blank" rel="noopener" >IsCameraOn.swift</a>)</em></li>
<li><strong>Focus mode</strong> or Do Not Disturb changes</li>
</ul>
<p>Thankfully I had already implemented those in <a href="https://github.com/alin23/mac-utils"  target="_blank" rel="noopener" >mac-utils</a>, but adapting them to Crank&rsquo;s event trigger architecture would be quite the work. By now, I learned that this is where Claude shines: once it has working code to reference, it does a really good job at adapting that code to another project&rsquo;s structure.</p>
<p>After I pointed Claude to the files and told it to create triggers out of them, I needed less than half an hour of testing and fixing logic bugs on my part to get everything in good order. It can write quite readable code when it wants.</p>
<p>I remembered seeing this crazy app that told you the <a href="https://github.com/samhenrigold/LidAngleSensor"  target="_blank" rel="noopener" >MacBook lid angle</a>, so I thought what the heck, someone might find a use for that. I told Claude to implement a lid angle trigger as well based on that code. I&rsquo;m thinking one might want to disconnect stubborn Bluetooth peripherals when the lid is closing, and you only have a chance to do that <strong>before</strong> the lid was fully closed.</p>
<p>Open source becomes incredibly more useful with an LLM to understand the code in seconds instead of days. It also feels like stealing, not gonna lie. If I did the research myself, it wouldn&rsquo;t have felt like that, and I can&rsquo;t figure out why.</p>
<h3 id="cherri">Cherri</h3>
<p>Some actions simply can&rsquo;t be done through scripts. One example of that is enabling Do Not Disturb (or changing the Focus Mode). There&rsquo;s no CLI that can do it because the system does not expose those APIs.</p>














<a href="https://alinpanaitiu.comimages/cherri-dnd.png">
  <figure>
    <figcaption>A cherri script on the left and the compiled Shortcut on the right</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 1000px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/cherri-dnd.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/cherri-dnd.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/cherri-dnd.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/cherri-dnd.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/cherri-dnd.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/cherri-dnd.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/cherri-dnd.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/cherri-dnd.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/cherri-dnd.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/cherri-dnd.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/cherri-dnd.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/cherri-dnd.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/cherri-dnd.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 1000px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/cherri-dnd.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/cherri-dnd.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/cherri-dnd.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/cherri-dnd.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/cherri-dnd.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/cherri-dnd.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/cherri-dnd.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/cherri-dnd.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/cherri-dnd.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/cherri-dnd.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/cherri-dnd.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/cherri-dnd.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/cherri-dnd.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 1000px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/cherri-dnd.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/cherri-dnd.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/cherri-dnd.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/cherri-dnd.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/cherri-dnd.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/cherri-dnd.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/cherri-dnd.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/cherri-dnd.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/cherri-dnd.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/cherri-dnd.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/cherri-dnd.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/cherri-dnd.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/cherri-dnd.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/cherri-dnd.png"
        alt="A cherri script on the left and the compiled Shortcut on the right"
        
        
        style="max-width: min(1000px, 90vw)"
      >
    </picture>
  </figure>
</a>

<p>But it can easily be done through Shortcuts, which has a <strong>Turn Do Not Disturb On/Off</strong> action built-in. <strong>But</strong> (big but&hellip;) a named Shortcut containing that action needs to already be created by the user, we can&rsquo;t call individual actions. And I can&rsquo;t have Crank ask the user to create Shortcuts and drag actions around for such basic things.</p>
<p>Shortcuts can be pre-created as <code>file.shortcut</code>, and installed by simply opening them, so I could provide those Shortcuts bundled with the app. But doing a rough calculation, I would have to create about 100 individual Shortcuts by hand, using mind numbingly slow drag and drop.</p>
<p>Programmer laziness got the best of me and I went looking for a quicker way. Somehow I stumbled on this gem: <a href="https://cherrilang.org/"  target="_blank" rel="noopener" >Cherri - a programming language that compiles directly to a signed Shortcut</a></p>
<p>I tried to get Claude to write a few basic Shortcuts with Cherri, but it clearly didn&rsquo;t know the syntax. So I told Claude to just learn it from the website, and gave it the whole list of actions I need.</p>
<p>In less than 10 minutes it:</p>
<ul>
<li>learned the Cherri programing language syntax</li>
<li>learned to use the <code>cherri</code> compiler</li>
<li>got action definitions with <code>cherri --action</code></li>
<li>wrote and compiled 98 individual Shortcuts, fully functional with inputs and outputs</li>
</ul>
<p><em>damn</em></p>
<h3 id="the-scripts-problem">The scripts problem</h3>
<p>The problem with creating an automation app, is that you usually have to spend most of your time building a huge library of built-in actions. I didn&rsquo;t want to go that route.</p>
<p>That&rsquo;s why Crank&rsquo;s actions seem pretty limited:</p>
<ul>
<li>Shell script</li>
<li>AppleScript</li>
<li>Shortcuts</li>
<li>Launch App</li>
<li>Open URL</li>
<li>Open File</li>
<li>Notification</li>
</ul>
<p>But in my opinion, this set of actions allows you to do most common things on a Mac, <em>if you know what to use</em>. However, not all users of this app will know what a script is, or how to write one, so I thought I should lower the barrier of entry as much as I can.</p>
<p>If LLMs can write such good code, why not have them write the automation scripts for us? <em>(well, because they could delete all our files with a misplaced semicolon, that&rsquo;s why&hellip;)</em> But non-devs would try an LLM anyway, so I thought I could at least make their life easier and add some safety guards.</p>
<p>In this test, I wanted to see if an LLM understands how to make another less capable LLM get good results.</p>
<p>So I told Claude to create a <code>context.md</code> describing common CLI utilities that would help in automating a Mac, and express that as a prompt for Gemini.</p>
<p>I added a <strong>Generate Code</strong> button in the script editor and tried to make the free Gemini tier write short useful scripts, instead of hundred-lines hallucinations on CLIs that don&rsquo;t exist.</p>
<p>It&rsquo;s not perfect, but when it works, it feels magical.</p>


  <figure style="width: min(1000px, 90vw); margin: auto">
    <video autoplay loop muted playsinline controls disablepictureinpicture style="width: 100%; margin: 10px auto; border-radius: 8px;"  >
      <source src="/video/crank-generate-code.mp4">
  </video>
  <figcaption>Generating a script to compress invoices and print them</figcaption>
  </figure>

<p>Turns out, LLMs know how to talk to other LLMs. The generated <code>context.md</code> tells Gemini things like:</p>
<blockquote>
<p>You are a macOS shell automation expert.<br>
&hellip;<br>
Output ONLY the script code — no markdown fences, no preamble, no explanation</p>
</blockquote>
<p>Claude even went as far as to customize the context based on the Mac it runs on:</p>
<blockquote>
<p>Assume the script will run on \(ARCH) Macs with macOS \(MACOS_VERSION)</p>
<p>Use the event variables set by Crank if relevant to the task</p>
<p>Tool Availability on this System:<br>
&hellip; <em>basic stuff like ffmpeg, jpegoptim etc.</em> &hellip;</p>
</blockquote>
<p>It&rsquo;s not complex code or writing, but not very creative either. And I like that I can delegate the boring parts to a smart text generator so I can focus on the harder reverse engineering and making Macs do things they weren&rsquo;t supposed to do.</p>
<p>At least until it learns to work with Frida and a disassembler and I&rsquo;m fully replaced.</p>
<h2 id="consequences">Consequences</h2>
<p>I started this exploration as an AI skeptic, or better said, ignorant. But things are progressing faster than I can adapt to them. So while I&rsquo;m no longer a skeptic, I&rsquo;ll probably stay ignorant in the long term.</p>
<p>I see Claude Code as a valuable tool for experienced devs, just like I saw syntax highlighting, autocomplete, IDEs and other dev tools when they appeared. It enables me to spend less time in front of a screen without neglecting the people using my app, which <strong>is</strong> my end goal.</p>
<p>But it is frightening to think; what would my mind go through if I was a junior dev in college and seeing a $20/month algorithm do what I&rsquo;d need to learn in years?</p>
<p>And now I&rsquo;m thinking <em>&ldquo;I&rsquo;m fine, Claude is smart but it&rsquo;s still far from being able to do all the complex work I do for creating a truly useful app&rdquo;</em>. But <em>far</em> is relative, and such a different measure in the world of AI.</p>
<p>I&rsquo;m probably in denial, I remember laughing at LLMs hallucinating non-existent Swift syntax less than 3 months ago.</p>
<hr>
<p>To calm some of the people using my apps who might also read this: I&rsquo;m not becoming a <em>vibe coder</em> and giving Claude free reign on Lunar and Clop.</p>
<p>At least at the moment, the codebases of those apps are still too messy and complex for an LLM, I don&rsquo;t want even the remote possibility of introducing a critical bug. rcmd still has a small codebase and doesn&rsquo;t work with hardware and sensitive files, so it&rsquo;s safer to try it there, but every line is still reviewed and validated by hand.</p>
<p>I&rsquo;ll probably try to get Claude to help me on things I started but never had the time to finish, like:</p>
<ul>
<li>building a custom filesystem index and fuzzy search algorithm for <a href="https://lowtechguys.com/cling"  target="_blank" rel="noopener" >Cling</a></li>
<li>adding natural-language calendar event scheduling in <a href="https://lowtechguys.com/grila"  target="_blank" rel="noopener" >Grila</a></li>
<li>fixing long-standing bugs in <a href="https://lowtechguys.com/startupfolder"  target="_blank" rel="noopener" >StartupFolder</a> and <a href="https://lowtechguys.com/istherenet"  target="_blank" rel="noopener" >IsThereNet</a></li>
</ul>
<p>But it will probably function more as a talking rubber duck, than as an employee I can trust to do my work for me.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>Developing a food-safe finish for my wooden spoons and cups</title>
      <link>https://alinpanaitiu.com/blog/developing-hardwax-oil/</link>
      <pubDate>Mon, 08 Dec 2025 18:16:44 +0200</pubDate>
      <author>alin@panaitiu.com (Alin Panaitiu)</author>
      <guid>https://alinpanaitiu.com/blog/developing-hardwax-oil/</guid>
      <description>
        <![CDATA[<p>You know what they say in the world of products: <em>fast, cheap, good; pick two</em></p>
<p>Which is very similar to my experience when trying to find the most suitable wood finish for my hand carved wooden spoons and coffee cups.</p>














<a href="https://alinpanaitiu.comimages/hand-carved-wooden-spoon.jpeg">
  <figure>
    <figcaption>hand carved wooden spoon</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 500px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 500px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 500px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hand-carved-wooden-spoon.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/hand-carved-wooden-spoon.jpeg"
        alt="hand carved wooden spoon"
        
        
        style="max-width: min(500px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>It&rsquo;s hard to find a finish that:</p>
<ul>
<li>cures fast <em>(in less than 2 days)</em></li>
<li>is food safe <em>(can be used on things you eat or drink from, like spoons and cups)</em></li>
<li>is free of solvents <em>(doesn&rsquo;t smell of nasty chemicals that make you dizzy every time you apply it)</em></li>
<li>and with the hard requirement of making wood hydrophobic and leaving a visually pleasant sheen without creating a plasticky layer</li>
</ul>
<h2 id="natural-drying-oils">Natural drying oils</h2>
<p>Sure, we have <strong>pure tung oil</strong> which doesn&rsquo;t need any solvent, has a pleasant nutty smell, and cures into a food-safe hydrophobic polymer inside the wood fibers. But it takes 2 to 4 weeks for the curing to happen depending on oxygen intake, and the finish is quite matte and boring.</p>
<blockquote>
<p>While tung oil is <em>natural</em>, that doesn&rsquo;t mean you can ingest it. It is a powerful irritant in its liquid form, it is only food-safe after curing into its inert polymer state.</p>
</blockquote>
<p>There’s also the more rare <strong>polymerized (or heat-bodied) tung oil</strong>, which is a honey-like viscous oil that cures in a few days because it was heated to very high temperatures in the absence of oxygen. That would be ideal except I can’t find it anywhere around me to buy, and it would most likely need thinning with citrus oil solvent (or D-limonene).</p>
<p>Citrus oil has a nice but way too potent smell of orange peel. It might seem pleasant at first compared to the chemical smell of naphtha and white spirits, but it can be too much if you finish small wooden objects often like I do.</p>














<a href="https://alinpanaitiu.comimages/ash-cup-oiled.jpeg">
  <figure>
    <figcaption>ash cup, in the process of being oiled with tung oil</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 500px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 500px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 500px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/ash-cup-oiled.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/ash-cup-oiled.jpeg"
        alt="ash cup, in the process of being oiled with tung oil"
        
        
        style="max-width: min(500px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>And there are plenty other drying oils, the most well-known being <strong>linseed oil</strong>, which is indeed edible but has the unfortunate disadvantages of yellowing over time, taking ages to cure and having a persistent oily-grassy flavor. Polymerized linseed oil (aka stand oil) cures faster but still retains the nasty flavor.</p>
<blockquote>
<p>Careful not to confuse it with <strong>boiled</strong> linseed oil or BLO, which uses metallic drying agents like cobalt or manganese salts.</p>
<p>The boiled name comes from the old practice of heating the linseed oil with lead oxide inside, effectively shortening the oxidation process. But nowadays there&rsquo;s no heat involved in BLO.</p>
<p>More details in <a href="https://blog.lostartpress.com/2024/03/03/the-lowdown-on-blo/"  target="_blank" rel="noopener" >The lowdown on BLO</a></p>
</blockquote>
<p>The oil painting world has experience with many drying oils like safflower oil, walnut oil, poppy seed oil, hemp seed oil. Unfortunately they cure slower and into a less durable film while also being rare and expensive so they aren&rsquo;t good alternatives for wooden utensils.</p>
<h2 id="hardwax-oils">Hardwax oils</h2>
<p>For a good few months I used <a href="https://www.osmo.com/finishes/interior-finishes/flooring/polyxr-oil-original"  target="_blank" rel="noopener" >Osmo Polyx Oil</a> which is a solvent-based hardwax oil. Its <a href="https://www.osmo.com/fileadmin/media/pim_assets/pim_file_5655.pdf"  target="_blank" rel="noopener" >safety datasheet</a> and <a href="https://ardec.ca/media/catalog/documents/VD_HWO_3011-3065_2020-GB.pdf?srsltid=AfmBOopJj0MPISz0WnVPHR9rLwgLjP2v-cv9kdV_jIyFPjHmy1rw31wC"  target="_blank" rel="noopener" >full declaration of ingredients</a> mention about 50% of it being white spirit solvent, and the rest is composed of natural oils processed to become drying oils, and a small amount of hard carnauba wax.</p>
<p>It is fast curing in less than a day, leaves a beautiful shimmer on the wood surface, it is food safe after curing, but smells awful because of the solvents.</p>
<p>In the winter months I carve indoors and have to finish the pieces indoors as well, and the horrible solvent smell fills my house for a whole day. I seem to have become sensitized to the smell and now I have almost a full can of Osmo that I can&rsquo;t use without a mask, even outdoors.</p>














<a href="https://alinpanaitiu.comimages/rubio-monocoat-board.jpeg">
  <figure>
    <figcaption>Rubio Monocoat two-component hardwax oil on a half finished wooden board</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 500px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 500px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 500px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/rubio-monocoat-board.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/rubio-monocoat-board.jpeg"
        alt="Rubio Monocoat two-component hardwax oil on a half finished wooden board"
        
        
        style="max-width: min(500px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>There&rsquo;s also <a href="https://www.rubiomonocoatusa.com/products/oil-plus-2c-130ml"  target="_blank" rel="noopener" >Rubio Monocoat</a> and other two-component hardwax oils where the base component is usually a solvent-free blend of drying oils and waxes, and the accelerator component is <em>Hexamethylene diisocyanate</em> or HDI. The base component can cure on its own in about 3 weeks and the accelerator shortens the curing time to less than a day.</p>
<p>This alternative cures fast into an inert and food safe polymer, smells like apple pie, and the finish has a slight satin effect. But it&rsquo;s really hard to mix properly and apply on small wooden objects like spoons and cups. I almost always use too much accelerator, the mixture ends up too viscous and hard to spread on the wood and the constant opening of the bottles and cans makes the components dry prematurely in their containers.</p>
<p>However, this idea of blending drying oils with hard wax seems to be promising and it&rsquo;s the path I continued on developing.</p>
<h2 id="notable-mentions">Notable mentions</h2>
<p><strong>Food-safe epoxy</strong> is another alternative, but I hate both the idea of using synthetic plastics and their plasticky look takes away from the experience of using wooden utensils.</p>
<p>Why even use wood if you&rsquo;re going to cover it in a layer of clear plastic?</p>
<p>Many people also recommend other edible oils like <strong>coconut oil</strong>, <strong>olive oil</strong> etc. But these don&rsquo;t cure and don&rsquo;t provide any protection for hot liquids. They either go rancid inside the wood fiber or get washed out after using the wooden object.</p>
<p>Some recommend non-edible petrol-based <strong>mineral oil</strong> (aka liquid parrafin) because it doesn&rsquo;t go rancid, but has the same effect of not actually doing much for protection and will leak into hot liquids.</p>
<p><strong>Beeswax</strong> is often used by kuksa makers <em>(traditional Scandinavian wooden cups)</em> but wax molecules are too large to penetrate wood fibers well, and it has a low melting point of 60℃ (140°F) so hotter liquids are out of the question.</p>
<p><strong>Carnauba wax</strong> has a higher melting point at 80℃ (176°F) and is harder than beeswax, but it has a glossy finish, having a similar look to epoxy resin after buffing.</p>
<p><em>The ideal finish should combine the durable hydrophobic properties of fiber-penetrating tung oil with the fast drying properties of a top wax layer, in an easy to apply but solvent-free blend.</em></p>
<h3 id="urushi">Urushi</h3>














<a href="https://alinpanaitiu.comimages/urushi-lacquered-spoons.jpeg">
  <figure>
    <figcaption>wooden spoons finished with urushi lacquer</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 500px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 500px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 500px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/urushi-lacquered-spoons.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/urushi-lacquered-spoons.jpeg"
        alt="wooden spoons finished with urushi lacquer"
        
        
        style="max-width: min(500px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>Some carvers use <a href="https://www.woodspirithandcraft.com/blog/urushi"  target="_blank" rel="noopener" >urushi lacquer</a> which is the sap from a tree common to Japan. The result is a hard and glossy finish that can resist temperatures of over 300℃ (572°F), but the look and process of application is far from what I&rsquo;m looking for.</p>
<p>To ensure curing is complete, it needs 10 to 20 very thin coats over many months in a warm and humid environment. The wood then takes this dark brown color, regardless of what wood you started with, which is the opposite of what I want.</p>
<p>I work with fruit and flower woods that are less common in woodworking (apple, fig, jasmine etc.) and I want people to know that and experience what that specific wood looks and feels like. I want to keep their original hues and tints and accentuate the wood grain of each piece.</p>














<a href="https://alinpanaitiu.comimages/fig-tree-spoon.jpeg">
  <figure>
    <figcaption>fig wood spoon between other hand carved spoons</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 500px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 500px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 500px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/fig-tree-spoon.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/fig-tree-spoon.jpeg"
        alt="fig wood spoon between other hand carved spoons"
        
        
        style="max-width: min(500px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>Someone from Israel bought a spoon I made from fig tree wood, but still asked about it a bit incredulously. It had greenish light wood grain, with a bit of untouched bark kept on the handle and a wonky curve that followed the shape of the tree branch it was made from.</p>
<p>They have many fig trees there but had never seen such a thing made from their wood, so they felt a connection to their motherland in that usual object.</p>














<a href="https://alinpanaitiu.comimages/lilac-cooking-spoon-front-back.jpeg">
  <figure>
    <figcaption>lilac coooking spoon, front and back</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 500px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 500px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 500px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/lilac-cooking-spoon-front-back.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/lilac-cooking-spoon-front-back.jpeg"
        alt="lilac coooking spoon, front and back"
        
        
        style="max-width: min(500px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>My mother-in-law cut a large lilac tree and gave me some thick branches. It&rsquo;s a wood that cracks very easily while drying, and I couldn&rsquo;t use most of it. But the few things I made from it, I gifted back to my wife&rsquo;s mother and sisters in the form of cooking spoons and are still cherished by them today.</p>
<p>Its pastel purple grain accents remind them clearly of the tree that used to flower every spring in their yard.</p>
<p>A brown lacquer would never be able to create  this effect, and that is why I&rsquo;m still trying to find the perfect natural oil based finish.</p>
<h3 id="hassui-ceramic-or-liquid-glass">Hassui Ceramic (or liquid glass)</h3>
<p>This is basically polysiloxane dissolved in alcohol and isoparaffin solvent. After 3 weeks it cures into a food-safe, hydrophobic layer which is durable, but not very good looking in my eyes.</p>
<p>It leaves the wood looking bare, without the color enhancing and rippling effect of oil. Some might prefer that, especially people that worked hard on getting a very shiny knife finish on their carving.</p>
<p>In my case, this is not the finish I&rsquo;m looking for because of the 3-week curing time, the solvent-based solution, the colorless look and the fact that this finish is very expensive and hard to find outside Japan.</p>
<p><em>Here&rsquo;s some more information from a woodturner&rsquo;s experience with the finish: <a href="https://www.bigsandwoodworking.com/liquid-glass-wood-finish-update/"  target="_blank" rel="noopener" >Liquid Glass Wood Finish – Update</a></em></p>
<h2 id="natural-hardwax-oil">Natural hardwax oil</h2>
<blockquote>
<p>For the impatient, here&rsquo;s a <a href="#comparison-video" >comparison video</a> on how the hardwax oil blends look and perform in a water test on some plum wood blanks.</p>
</blockquote>
<p>My experiments started simple, with melting about 10g of <strong>carnauba wax</strong> flakes into 40g of pure <strong>tung oil</strong>.</p>














<a href="https://alinpanaitiu.comimages/tung-carnauba.jpg">
  <figure>
    <figcaption>Tung oil and carnauba wax flakes</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/tung-carnauba.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/tung-carnauba.jpg"
        alt="Tung oil and carnauba wax flakes"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<blockquote>
<p>Most people recommend a double-boiler or bain-marie approach to melting the wax. That&rsquo;s because the heat source would be a container of boiling water which can never get above 100℃ (212°F), a safe temperature to avoid reaching the flash point of these oils.</p>
<p>I just drop a spoonful of carnauba flakes into a glass jar of tung oil and place it in the microwave for a minute until everything becomes liquid.</p>
<p>For small quantities, the microwave works wonders, and there&rsquo;s no danger of overheating the oil or gelifying it because it cools off rapidly.</p>
</blockquote>
<hr>
<h3 id="experiment-1">experiment #1</h3>
<p><strong>Ingredients:</strong></p>
<ul>
<li>40g pure tung oil</li>
<li>10g carnauba wax flakes</li>
</ul>
<p><strong>Process:</strong></p>
<ul>
<li>pour the tung oil into a glass jar or ceramic container</li>
<li>drop the carnauba wax flakes into the oil and stir to disperse the flakes</li>
<li>place in the microwave for 30 seconds then stir again for a few seconds</li>
<li>repeat until the wax melts and everything is liquid</li>
<li>let it cool down until the mix is solid</li>
</ul>
<p><strong>Result:</strong></p>
<p>Brittle paste that&rsquo;s hard to scoop from the jar and apply on the wood. Doesn&rsquo;t spread easily, feels sticky.</p>
<p>After the whole wood surface was covered and left for 10 minutes, the excess was wiped with a paper towel. After wiping, the finish is very matte, which means that not much of the wax remained, and the oil didn&rsquo;t penetrate the fiber enough because of the consistency.</p>
<p>Smells nice though.</p>
<h4 id="improving-the-consistency">Improving the consistency</h4>
<p>The consistency changes a lot based on the oil-to-wax ratio.</p>
<p>Too much wax and the blend cools into a brittle paste that&rsquo;s very hard to scoop from the jar. Too little and the blend is too liquid with not enough wax remaining on the wood surface to form a film. A 4:1 oil-to-wax ratio is good, having enough wax for the top layer, but in this simple formulation it is still too hard and brittle.</p>
<p>Instead of increasing the oil quantity which could defeat the purpose of the wax layer, I added a bit of <strong>beeswax</strong> to soften the blend.</p>
<p>This works, the paste becomes softer but it  makes the top layer of wax not durable enough because of its lowered melting point, so I needed an alternative.</p>
<p><em>My idea is to have the wood absorb the tung oil from the blend while leaving the wax molecules on the wood surface. The wax should be soft enough to not create a glossy layer and not be brittle, but hard enough to protect the wood for the time it takes the tung oil to cure.</em></p>
<hr>
<h3 id="experiment-2">experiment #2</h3>
<p><strong>Ingredients:</strong></p>
<ul>
<li>40g pure tung oil</li>
<li>7g carnauba wax flakes</li>
<li>2g beeswax</li>
<li>1g lanolin</li>
</ul>
<p><em>To soften the blend without having to add too much beeswax, I found <strong>lanolin</strong> to be a good addition. It is a highly hydrophobic wax extracted from sheep wool that even in tiny amounts can make the paste consistency softer and easier to apply.</em></p>
<p><strong>Process:</strong></p>
<p>Similar to #1, just immerse the waxes into the oil in a glass jar and microwave in increments of 30 seconds until everything is liquid. Leave to cool until solid.</p>
<p><strong>Result:</strong></p>
<p>Softer and more homogenous paste that can be scooped easily from the jar with a finger or rag. Spreads relatively easy onto the wood surface, feels like a more viscous hand cream.</p>
<p>Smells amazing, nutty, vanilla and honey scent, faint enough to not bother anyone.</p>
<p>After covering the whole wood surface, I used a heat gun to liquify the finish and enhance wood fiber penetration. The paste melts very easily and the oil in it gets absorbed quickly in the wood pores, leaving the wax to solidify.</p>
<p>After the wood has cooled, I applied another layer because it seemed that some parts of the wood had little to no wax remaining. I removed the excess with a paper towel and buffed the wax to a satin look.</p>
<h5 id="small-issues">small issues</h5>
<h6 id="curing-speed">curing speed</h6>
<p>This finish is not fast <strong>curing</strong> by any means. It is fast <em>drying</em>, as the oil gets absorbed and the wax on top solidifies into a very thin medium-hard layer.</p>
<p>But the melting point of the wax is about 75℃ (167°F) so still not perfect for very hot liquids. You definitely can&rsquo;t brew your tea directly into a cup finished this way, not until the oil cures completely after 2-4 weeks.</p>
<p>I tested it by brewing a cup of espresso which seems to be fine, the temperature of the coffee is far below 75℃ (167°F) when it reaches the cup.</p>
<p>Using a spoon for eating soup also works fine, because the soup needs to be below 45℃ (113°F) for me to be able to eat it.</p>


<style>
  .responsive-flex {
    width: 100%;
    display: flex;
    justify-content: space-around;
    flex-direction: row;
  }

  @media (max-width: 768px) {
    .responsive-flex {
      flex-direction: column;
      align-items: center;
    }
  }
</style>

<div class="responsive-flex">















<a href="https://alinpanaitiu.comimages/olive-spoon-hydrophobic.jpeg">
  <figure>
    <figcaption>olive wood spoon with hydrophobic layer</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 400px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 400px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 400px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/olive-spoon-hydrophobic.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/olive-spoon-hydrophobic.jpeg"
        alt="olive wood spoon with hydrophobic layer"
        
        
        style="max-width: min(400px, 80vw)"
      >
    </picture>
  </figure>
</a>
















<a href="https://alinpanaitiu.comimages/sycamore-cup-hydrophobic.jpeg">
  <figure>
    <figcaption>sycamore coffee cup with hydrophobic layer</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 400px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 400px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 400px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sycamore-cup-hydrophobic.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/sycamore-cup-hydrophobic.jpeg"
        alt="sycamore coffee cup with hydrophobic layer"
        
        
        style="max-width: min(400px, 80vw)"
      >
    </picture>
  </figure>
</a>

</div>

<p>After usage, the wooden spoon and cup could be easily washed with warm water and soap, the wood didn&rsquo;t stain at all. The wax still worked as I could see the water droplets sliding off the wood.</p>
<h6 id="sheen-and-appearance">sheen and appearance</h6>
<p>Visually, it doesn&rsquo;t leave a very strong sheen. It has a soft waxy look, instead of the shimmer of oiled wood.</p>
<p>That&rsquo;s because wax doesn&rsquo;t follow the micro porous surface of the wood, it solidifies into a smooth surface. So instead of scattering light you get mirror-like reflections creating that glossy wax look.</p>
<p>Commercial finishes use flattening agents like fumed silica to get around this, but I&rsquo;m trying to use as little additives as possible for now.</p>
<p>The look also comes from the carnauba wax being softened by the beeswax and lanolin which also translates into a softer top layer.</p>
<h6 id="natural-resins">natural resins</h6>
<p>The woodworking world has moved on almost completely from natural resins. Almost every finish is either based on polyurethane or epoxy resin if you&rsquo;re looking for a harder top layer. At best, you&rsquo;re looking at a shellac layer which doesn&rsquo;t mix with oils.</p>
<p>Thankfully oil painting forums and blogs still have a trove of information on tree resins, drying oils and siccatives.</p>














<a href="https://alinpanaitiu.comimages/damar-resin-crystals.jpeg">
  <figure>
    <figcaption>damar resin crystals</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 500px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 500px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 500px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/damar-resin-crystals.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/damar-resin-crystals.jpeg"
        alt="damar resin crystals"
        
        
        style="max-width: min(500px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>That&rsquo;s how I found out about damar resin, a semi-hard resin extracted from <em>Dipterocarpaceae</em> trees growing in southeast Asia. It is a clear resin, melts at about 120℃ (248°F) which is easily attainable in a microwave, and has a bit of flexibility so that it doesn&rsquo;t create a cracking layer on wood.</p>
<p>It can be found in the form of <em>crystals</em> or <em>pellets</em> in art shops and it&rsquo;s pretty cheap. People dissolve it in turpentine to create varnishes, but I wanted to incorporate it into my oil-wax blend through heat instead of solvents.</p>
<hr>
<h3 id="experiment-3">experiment #3</h3>
<p><strong>Ingredients:</strong></p>
<ul>
<li>50g pure tung oil</li>
<li>10g carnauba wax flakes</li>
<li>2g beeswax</li>
<li>2g lanolin</li>
<li>3g damar resin</li>
<li>5g coconut oil</li>
<li>2 drops of MnZrCa drier</li>
<li>2 drops of vitamin E</li>
</ul>
<p><em>To increase sheen and make the top layer stronger, I melted damar resin crystals directly into oil, without any solvent. I also added a very small amount of manganese, zirconium and calcium drier to make the curing faster, and some vitamin E to increase shelf life.</em></p>
<p><strong>Process:</strong></p>
<p>Add the carnauba wax, beeswax and lanolin into the tung oil in a glass jar and microwave in increments of 30 seconds until everything is liquid.</p>
<p>Add the damar resin crystals into the coconut oil and microwave in increments of 10 seconds until everything is liquid. Let the mix cool down for about 30 seconds then add the drops of MnZrCa drier and vitamin E. Stir until the mix is homogenous.</p>
<p>Pour the damar resin-oil blend into the tung oil blend and stir until homogenous. Let cool down until solid.</p>
<p><strong>Result:</strong></p>
<p>Medium-soft paste that can be scooped relatively easily from the jar with a finger or rag. Spreads easy enough onto the wood surface.</p>
<p>Smells the same as the last experiment: nutty, vanilla and honey scent, faint enough to not bother anyone. The nasty smell of the drier doesn&rsquo;t transfer into the blend because its quantity is too small. The resin has no smell in its solid form.</p>
<p>After covering the whole wood surface, I used a heat gun to liquify the finish and enhance wood fiber penetration. The paste melts very easily and the oil in it gets absorbed quickly in the wood pores, leaving the wax and resin to solidify into a very thin layer on top.</p>
<p>After that there was almost no excess left and I buffed the top layer to a satin look using a paper towel.</p>
<p><strong>Curing:</strong></p>
<p>The tung oil inside the wood cured in about 48 hours.</p>
<p>This can be verified by melting the wax layer with a heat gun then removing the liquid wax with a rag. The bare wood then can be sanded with 240 grit to check if the wood dust falls off easily. If the wood dust seems wet and gums on the sandpaper, the oil is still not completely cured.</p>
<p><strong>Look:</strong></p>
<p>The satin sheen looks better because of the resin. Regions with medullary rays look wavy and move in the light, regions with straight fibers look exactly between glossy and matte.</p>
<p>Osmo Polyx Oil still has a prettier shimmer that this finish doesn&rsquo;t achieve, I&rsquo;ll look into adding trace amounts of fumed silica to see if it makes the finish settle better into the wood fiber microstructure and scatter light instead of being more on the smooth glossy side.</p>
<p>The fumed silica might also help with adding shear thinning capability so that the paste is even easier to scoop from the jar without softening it.</p>
<h4 id="notes-on-added-ingredients">notes on added ingredients</h4>
<h5 id="metallic-driers">metallic driers</h5>
<p>The metallic driers are probably the first thing that jump out, because we always read about how bad they are when seeing people talk about natural wood finishes. Not all driers are bad, and it depends on things like quantity, leaching probability and type of metal used.</p>
<p>Sure, lead and cobalt driers are out of the question, I don&rsquo;t want them anywhere near my food and beverages no matter how small the risk of leaching. They contain heavy metals that can have catastrophic effects if they accumulate in your body.</p>
<p>I use a drier containing manganese neodecanoate to speed up surface-drying, and zirconium and calcium salts for through-drying.</p>
<hr>
<p>Zirconium and calcium are known to be safe. Zirconium is the same metal that dental implants are usually made of, even ceramic knives are made using zirconium, it seems to be a pretty inert metal when it comes to contact with our body.</p>
<p>Manganese is toxic if inhaled or ingested in high amounts, but the quantity of manganese in 2 drops of drier is so incredibly low you could take them orally and not feel any effect other than the horrible solvent taste. That, combined with the fact that the drier disperses throughout the oil makes the quantity too low to matter.</p>
<p>The strictest <a href="https://efsa.onlinelibrary.wiley.com/doi/10.2903/j.efsa.2023.8413"  target="_blank" rel="noopener" >Tolerable Upper Intake Level for manganese</a> is set to 8 mg/day. In fact we get some of that quantity daily through food that is enriched with vitamins and minerals. If the drier were to ever leach completely from the finish, one would get less than 0.1mg per lifetime of object.</p>
<blockquote>
<p>You&rsquo;d probably have to eat the spoon completely for that to happen, in which case the quantity of ingested wood might become more of a problem.</p>
</blockquote>
<p>But the whole food-safe standard on drier based finishes is actually based on the fact that a cured finish traps that metal into an inert polymer which can&rsquo;t leach into food. Just like Teflon pans or aluminum and copper pots, everything is safe until the layer chips off somehow.</p>
<p>I compromised on having a quantity small enough to not pose any safety risk and that wouldn&rsquo;t shorten shelf life by making the oil cure inside the jar, but high enough to make the oil cure in 48 hours instead of 2-4 weeks. This, in my opinion, makes the finish safer by being more predictable in curing time, and minimizing the probability of food contacting uncured tung oil.</p>
<h5 id="coconut-oil-and-vitamin-e">coconut oil and vitamin E</h5>
<p>Because damar resin melts at 120℃ (248°F), tung oil is not suitable for melting the resin directly into it because tung oil can gelify at high temperatures, hindering its curing capabilities. That&rsquo;s why I melt the resin crystals in a small amount of coconut oil, which has a high enough smoking point of 180℃ (356°F), smells amazing and doesn&rsquo;t go rancid easily.</p>














<a href="https://alinpanaitiu.comimages/oil-smoke-point-chart.png">
  <figure>
    <figcaption>oil smoke point chart</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 350px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 350px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 350px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oil-smoke-point-chart.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/oil-smoke-point-chart.png"
        alt="oil smoke point chart"
        
        
        style="max-width: min(350px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>I added 2 drops of vitamin E (aka tocopherol) because it is a powerful antioxidant and will lower the chance of ingredients like coconut oil going rancid. It can also lower the chance of the tung oil curing inside the container by inhibiting the autoxidation reaction.</p>
<p><em>This is the current formulation that I&rsquo;m using for finishing my wooden spoons and cups, and which I&rsquo;m selling on my <a href="https://gospodaria.com/products/ceara-naturala-pentru-lemn?variant=56081719427404"  target="_blank" rel="noopener" >online shop</a> in Romania.</em></p>
<hr>
<h3 id="comparison-video">Comparison video</h3>
<p>In the following video, I applied the 3 experiments on some plum wood blanks, sanded to 600 grit and water popped 3 times.</p>
<p>In total I had 7 blanks:</p>
<ol>
<li><a href="#experiment-1" >experiment #1</a></li>
<li><a href="#experiment-2" >experiment #2</a></li>
<li><a href="#experiment-4" >experiment #3</a></li>
<li>unfinished wood</li>
<li><a href="#hardwax-oils" >Osmo Polyx Oil</a></li>
<li><a href="#natural-drying-oils" >raw tung oil</a></li>
<li><a href="#metallic-driers" >raw tung oil with drier</a></li>
</ol>
<p>After applying the finishes, I left them for 24 hours to cure indoors at 22℃ (72°F) at 50% humidity, then sprayed them with water to see how the water beaded up on the surface.</p>


  <figure style="width: min(500px, 80vw); margin: auto">
    <video controls disablepictureinpicture style="width: 90%; margin: 10px auto; border-radius: 8px;"  >
      <source src="/video/hardwax-oil-blend-comparison.mp4">
  </video>
  <figcaption>Comparison between hardwax oil blends</figcaption>
  </figure>

<h4 id="conclusions">Conclusions:</h4>
<p>Osmo Polyx still creates the highest sheen, but felt a bit too glossy on this specific wood, not sure why. I actually liked the damar resin and drier blend the most.</p>
<p>Osmo contains titanium dioxide to whiten the finish a bit, and it also contains more refined oils, so the wood colors looked less saturated. I guess that&rsquo;s good if you want to keep a light wood from getting a yellow tint, but on my spoons and cups I very rarely need that, so I like the colour imparted by the tung oil.</p>
<p>Water beaded up on all finished blanks and was absorbed by the unfinished wood instantly as expected. Osmo and our #3 blend had the largest water beads.</p>
<p>After cleaning up the water with a paper towel and letting the wood dry, the first two blends lost their sheen and colour almost completely and felt less smooth to the touch. The damar resin and drier really did their job in the third blend.</p>
<p>The tung oil only blank felt the roughest after drying, the oil only created a very weak hydrophobic layer because it couldn&rsquo;t cure after 24 hours. The blank with the drier added felt smoother, but still absorbed a bit of water. It needed more layers and more time to get full fiber saturation.</p>
<h3 id="experiment-4">experiment #4</h3>
<p><em>to be continued&hellip;</em></p>
]]>
      </description>
    </item>
    
    <item>
      <title>In search of the simplest all-in-one blade sharpener</title>
      <link>https://alinpanaitiu.com/blog/making-my-own-sharpening-blocks/</link>
      <pubDate>Fri, 11 Oct 2024 22:54:06 +0300</pubDate>
      <author>alin@panaitiu.com (Alin Panaitiu)</author>
      <guid>https://alinpanaitiu.com/blog/making-my-own-sharpening-blocks/</guid>
      <description>
        <![CDATA[<p>When I started wood carving, the only sharpening method I remembered was from seeing my mother use some kind of smooth broken stone that she passed over the length of the knife blade before sacrificing a chicken.</p>
<p>I also remember seeing my father use a very coarse stone wheel placed on a motor shaft which threw many sparks when he sharpened some large axe for splitting wood.</p>
<p>I had neither of those around anymore in my rented place in the city so I jumped headfirst in the mind numbing and sometimes esoteric art of getting a sharp blade.</p>














<a href="https://alinpanaitiu.comimages/old-sharpening-methods-from-parents.png">
  <figure>
    <figcaption>crude sharpening methods that my parents used</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/old-sharpening-methods-from-parents.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/old-sharpening-methods-from-parents.png"
        alt="crude sharpening methods that my parents used"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<h2 id="carving-knives">Carving knives</h2>
<p>The first blade type I had to sharpen was for my <a href="https://beavercrafttools.com/products/s10-wood-carving-set-of-12-knives"  target="_blank" rel="noopener" >BeaverCraft carving knives</a>.</p>
<p>They fortunately came with a <strong>strop</strong>, basically a plywood base in the form of a paddle, with leather stuck to it on both sides, and a green waxy bar. Unfortunately I had no idea what to do with it.</p>














<a href="https://alinpanaitiu.comimages/beavercraft-carving-knives.jpeg">
  <figure>
    <figcaption>BeaverCraft carving knive set</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/beavercraft-carving-knives.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/beavercraft-carving-knives.jpeg"
        alt="BeaverCraft carving knive set"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p><em>Stropping</em> is, at the most basic level, dragging the blade back and forth on a semi-hard surface (like leather). Clean leather won&rsquo;t make your blade sharper though, and that&rsquo;s why you have that green bar which you need to rub onto the leather. It contains a fine abrasive that slowly removes tiny amounts of steel from the very tip of your blade to make it sharper.</p>
<p>Reading about stropping and watching videos about it gets into a lot of debate on:</p>
<ul>
<li>how often to strop</li>
<li>on what angle should the blade be held on the leather</li>
<li>should you move from the heel of the blade to the tip, or the reverse</li>
<li>can you strop too much and round the apex</li>
<li>what is the green compound for and how should it be applied</li>
</ul>
<p>And so on. It’s hard to find much exact science on this, everyone figures it out as they go, and <em>experts</em> share their beliefs based on their extensive experience.</p>
<p>I settled on stropping after every 15 minutes of constant carving, but I had no idea what I was doing or if I got any result.</p>
<p>That’s one thing I learned that would have relieved me of much frustration at the start: <strong>find a way to test the outcome of your motions, otherwise you’ll just keep repeating things that don’t work</strong></p>
<blockquote>
<p><strong>And it also applies to programming:</strong> so many times I’ve seen colleagues writing code but with fear and uncertainty as they didn’t know that what they did would have the expected result. And only because they didn’t know how to test how the code would work.</p>
</blockquote>
<blockquote>
<p>For example they were backend programmers, that only knew how to run their backend <code>python</code> server, but had no idea how to run the whole stack to check if the front end did work correctly with their changes.</p>
</blockquote>
<blockquote>
<p>Or app devs who didn’t know how to use a local database, and instead always feared release day because of   how their changes will impact the production db.</p>
</blockquote>
<p>Nowadays I know how to test for sharpness.</p>
<p>If I want a basic working blade I test if the knife cuts printer paper easily and without tearing it. If I want a carving blade, I check if it pops hairs off my arm easily. And I check that often so I don’t do hundreds of blade passes for nothing.</p>
<hr>
<p>Anyway, back to my carving knives. For a long time I did a pretty bad job at keeping them sharp.</p>
<p>I would carve for a while then feel the need to push too much into the blade or get tear out in the wood, then I would move the blade back and forth on the strop a few dozens of times, then getting back to carving I would notice a slight improvement which went away after just a few cuts, then back to stropping a thousand times for nothing.</p>
<p>Stropping is not sharpening, and after so many cuts those blades needed serious sharpening which a piece of leather doesn’t do.</p>
<blockquote>
<p>Well, actually, stropping is sharpening, but it’s a very very fine kind of sharpening. It’s similar to using a super fine grit stone, something between 14000 and 100000 grit.</p>
<p>The green paste is a super fine abrasive that does remove metal from the apex of the blade, but so little at a time that I would need to do thousands of passes to get the same result that a coarse grit sharpening stone would do in a few passes.</p>
</blockquote>
<p>I started looking into sharpeners, and because &ldquo;stones&rdquo; felt like something old which only my mother used because she didn’t have any better method, I would look into &ldquo;modern&rdquo; sharpeners like:</p>
<ul>
<li><a href="https://www.worksharptools.com/products/pivot-pro-knife-sharpener"  target="_blank" rel="noopener" >pull-through V-blades</a></li>
<li><a href="https://www.worksharptools.com/products/ceramic-honing-rod"  target="_blank" rel="noopener" >honing rods</a></li>
<li><a href="https://www.worksharptools.com/products/ken-onion-edition-knife-tool-sharpener-mk-2%E2%84%A2"  target="_blank" rel="noopener" >powered belt grinders</a></li>
<li><a href="https://www.worksharptools.com/products/professional-precision-adjust-knife-sharpener"  target="_blank" rel="noopener" >precision sharpening rigs</a></li>
<li><a href="https://www.worksharptools.com/products/rolling-knife-sharpener"  target="_blank" rel="noopener" >rolling sharpeners</a></li>
</ul>














<a href="https://alinpanaitiu.comimages/modern-sharpening-methods.png">
  <figure>
    <figcaption>modern sharpening methods I tried</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/modern-sharpening-methods.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/modern-sharpening-methods.png"
        alt="modern sharpening methods I tried"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>I tried a few of those expensive methods, ruined a few blades with them, wasted days on reading reviews and watching tutorials&hellip; none of those methods felt like a deterministic way to get a sharp blade every time.</p>
<p>The precision sharpening rig is probably the closest to that, but I don&rsquo;t want to waste precious workbench space on that. Also, I can&rsquo;t take it with me when traveling.</p>
<h2 id="wood-planes-and-chisels">Wood planes and chisels</h2>
<p>Once I got into larger projects, I discovered how to use wood planes <em>(which seems to be thought of as an antiquated method in Romania, manual woodworking is dead here)</em>. I also discovered they need frequent sharpening and their blade shape seemed to be perfect for using a flat surface sharpening method like, you know, a stone.</p>














<a href="https://alinpanaitiu.comimages/tiny-block-plane.jpeg">
  <figure>
    <figcaption>tiny block plane I use for chamfering edges</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/tiny-block-plane.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/tiny-block-plane.jpeg"
        alt="tiny block plane I use for chamfering edges"
        
        
        style="max-width: min(500px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>Every woodworker on YouTube showed how to sharpen planes on stones so I thought I should probably get one eventually. But which type?</p>
<p>There are whetstones, natural stones, ceramic stones, oiled stones, diamond stones, you name it.</p>
<p>After trying a ceramic stone which needed to be soaked in water for 5 minutes before doing any sharpening, and constantly wetted between every few passes, and after making a wet mess and chipping the stone and dreading to sharpen because I had to keep the stone in water, I started looking into drier methods.</p>
<p><strong>Diamond stones</strong> seemed to be more up my alley: a modern method based on an old, tried and true idea.</p>
<blockquote>
<p>Whetstones and oiled stones are great as well, I just wanted something with less maintenance and more resistant to abuse.</p>
</blockquote>
<p>At this point I got sick of wasting money on this hobby, so I got the cheapest set of Chinese diamond plates online. It came in a set of 4 thin steel plates with foam backing, each plate having a different grit: 400, 600, 1000 and 1200. <em>Let’s not get into <a href="https://www.gritomatic.com/pages/grit-fundamentals?srsltid=AfmBOorYfnJneTYSmm5CabLuqF8WgBGAGVSZg441jcCUAHqRQYcBUYSK"  target="_blank" rel="noopener" >grit standards</a>, it’s probably FEPA but who knows.</em></p>
<p>And what do you know, it worked, it was easy to use, cheap and no special instructions needed.</p>














<a href="https://alinpanaitiu.comimages/cheap-diamond-plates.jpeg">
  <figure>
    <figcaption>cheap diamond sharpening plates</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/cheap-diamond-plates.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/cheap-diamond-plates.jpeg"
        alt="cheap diamond sharpening plates"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>I also got lucky in finding out about the <a href="https://youtu.be/pagPuiuA9cY?si=0wNNo5_CaKqrz6Lb"  target="_blank" rel="noopener" >OUTDOORS55</a> YouTube channel and the <a href="https://scienceofsharp.com"  target="_blank" rel="noopener" >science of sharp</a> website who do microscope analysis of blades and methods of sharpening.</p>
<p>They&rsquo;re getting as close as possible to actually knowing what the heck happens when you move the blade on top of a stone, leather, denim or any type of material people have tried sharpening on.</p>
<p>This helped me cut through the bullshit fast and get to a simple working method of getting any blade from rusty to shaving sharp in a few minutes.</p>
<h2 id="my-own-sharpening-blocks">My own sharpening blocks</h2>
<p>At this point I had these diamond plates scattered on my workbench, I had a leather strop, a green stropping compound bar that painted my fingers green every time I used it, a round diamond file for my hook knives <em>(because you can’t sharpen the inside of curved blades on a flat stone)</em> and a piece of flexible leather for stropping those hook knives.</p>
<p>It worked, but it was a mess. And I also work on the go a lot, I always do some wood project at my parents house, carve some branch at a walk in the woods or need to sharpen both a kitchen knife indoors and a chisel outdoors. I wanted to simplify this, but could not find any ready made product I wanted.</p>
<p>So I got to doing what I know best, half-assing an improvised product that works for me but would be ashamed of showing it to anyone.</p>
<h3 id="first-iteration">First iteration</h3>
<p>I cut a piece of <code>18mm</code> thick <a href="https://www.wood-database.com/european-beech/"  target="_blank" rel="noopener" >beech wood</a> into the shape and size of a diamond plate, then stuck a <code>600 grit</code> plate on one side, and the leather on the other side.</p>














<a href="https://alinpanaitiu.comimages/beech-block-leather-strop.jpeg">
  <figure>
    <figcaption>beech block with leather strop</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/beech-block-leather-strop.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/beech-block-leather-strop.jpeg"
        alt="beech block with leather strop"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>This not only got the stone and strop higher up from the table which helped a lot with sharpening long knives, but also put them in the same package which I could carry with me.</p>
<p>I called this a <strong>sharpening block</strong> in my mind.</p>
<p>To keep the block from slipping while I moved the blade over it, I cut a piece from a non-slip silicone baking mat. Even the small pressure of the blade makes the silicone adhere well to both the block and whatever surface it&rsquo;s sitting on.</p>














<a href="https://alinpanaitiu.comimages/silicone-nonslip-mat.jpeg">
  <figure>
    <figcaption>non-slip silicone baking mat piece</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/silicone-nonslip-mat.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/silicone-nonslip-mat.jpeg"
        alt="non-slip silicone baking mat piece"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>I still didn’t know what to do if I needed a coarser/finer grit, what to do about the green waxy mess of the stropping compound, and how to carry the hook knife sharpeners.</p>


  <figure style="width: min(300px, 80vw); margin: auto">
    <video controls loop muted playsinline disablepictureinpicture style="width: 90%; margin: 10px auto; border-radius: 8px;"  >
      <source src="/video/first-sharpening-block-beech.mp4">
  </video>
  <figcaption>The first sharpening block, beech wood base, diamond plate, leather strop</figcaption>
  </figure>

<h3 id="second-iteration">Second iteration</h3>
<p>This summer, my sister-in-law visited from Italy and asked if I could make a coffee table for her. Timing was tight because I had to make it in a week so she could transport it from Romania back to Italy by car.</p>
<p>I had no time to find wood slabs so I got two <a href="https://www.wood-database.com/red-oak/"  target="_blank" rel="noopener" >oak wood</a> panels from a big-box store, glued them one on top of the other to make the table-top thicker, and figured I&rsquo;ll find some table legs afterwards.</p>
<p>I found some <code>15cm</code> diameter smooth beech logs at a firewood seller nearby. Turns out they were leftovers from a veneer factory that can take a really long and thin log slice automatically, leaving the log core as smooth as a turned piece.</p>


  <figure style="width: min(300px, 80vw); margin: auto">
    <video controls loop muted playsinline disablepictureinpicture style="width: 90%; margin: 10px auto; border-radius: 8px;"  >
      <source src="/video/veneer-log-left-over.mp4">
  </video>
  <figcaption>Making veneer out of tree logs</figcaption>
  </figure>

<p>The table turned out pretty nice and solid, her children love sitting on top of it or hiding under it with their toys. I guess it could also be used for holding coffee cups, they didn&rsquo;t get a chance to try that yet.</p>














<a href="https://alinpanaitiu.comimages/oak-coffee-table-italy.jpeg">
  <figure>
    <figcaption>oak coffee table with beech wood legs</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oak-coffee-table-italy.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/oak-coffee-table-italy.jpeg"
        alt="oak coffee table with beech wood legs"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>So this left me with some <code>36mm</code> thick oak wood that I thought could make for a better sharpening block.</p>
<p>This time, I glued two coarse and fine grit plates back to back (either <code>400/1000</code> or <code>600/1200</code>) and embedded some neodymium magnets inside the wood to keep the diamond steel plates firmly attached but able to flip easily.</p>














<a href="https://alinpanaitiu.comimages/oak-block-magnets.jpeg">
  <figure>
    <figcaption>oak block with embedded magnets to hold the diamond steel plate</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oak-block-magnets.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/oak-block-magnets.jpeg"
        alt="oak block with embedded magnets to hold the diamond steel plate"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>For the strop, I didn’t have any leather left and it was crazy expensive, so I looked into alternatives.</p>
<p>You can basically use any semi-hard porous surface for a strop, even plain thick cardboard. It just needs to hold the fine particles of the compound and not flex too much when pressing the blade into it. People seem to use leather, denim, cotton, felt, cardboard, balsa wood etc.</p>
<p>After building my workbench, and the leg vise for it which needed some <strong><a href="https://benchcrafted.com/products/crubber"  target="_blank" rel="noopener" >rubberized cork</a></strong> for the vise faces, I now found myself with <code>3m²</code> of that rubberized cork on my hands because I could only buy it in bulk here.</p>
<p>I tested the cork for stropping and I was amazed to see it’s even better than leather:</p>
<ul>
<li>it holds more compound which makes it last more</li>
<li>the bars of waxy compound break down easier when dragging them across cork, almost like drawing with wax crayons. On leather I needed to heat the bars often.</li>
<li>it doesn’t get cut as easily. I would often drag the blade wrongly across the leather, cutting into it. The cork is like a self-healing material where cuts disappear</li>
<li>it’s cheaper</li>
<li>it can be shaped by sanding, which allows me to give the strop rounded edges that are great for stropping <a href="https://youtu.be/kYT_EE__hX0?si=MJqHOGeuqPsEJJig"  target="_blank" rel="noopener" >recurve blades</a> (those knives which curve to the inside and annoyingly only make contact with the edge of the strop)</li>
<li>it doesn’t dry and curl like leather</li>
</ul>
<p>So I cut and glued a <code>3mm</code> thick piece of that cork on the other side of the oak base and loaded it with pink corundum stropping compound.</p>
<p><em>Yes, this is the time I discovered the even more esoteric world of stropping, lapping and honing compounds which are not green bars of waxy stuff. We’ll get to that.</em></p>














<a href="https://alinpanaitiu.comimages/oak-block-cork.jpeg">
  <figure>
    <figcaption>rubberized cork strop with pink compound</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oak-block-cork.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/oak-block-cork.jpeg"
        alt="rubberized cork strop with pink compound"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>The <code>36mm</code> thick oak gave me enough space to drill <code>20mm</code> diameter holes with a forstner bit, where I could place a wooden dowel lined with that same cork for honing the hook knives. Another hole was for the stropping compound.</p>














<a href="https://alinpanaitiu.comimages/oak-block-compound-hole.jpeg">
  <figure>
    <figcaption>stropping compound and round strop embedded in the block</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/oak-block-compound-hole.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/oak-block-compound-hole.jpeg"
        alt="stropping compound and round strop embedded in the block"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>I finally reached an all-in-one sharpening block, super stable, with coarse enough grit for getting a bad blade into shape fast, fine grit for sharpening, enough strop for a few years and a way to sharpen recurves and hook knives.</p>


  <figure style="width: min(640px, 80vw); margin: auto">
    <video controls loop muted playsinline disablepictureinpicture style="width: 90%; margin: 10px auto; border-radius: 8px;"  >
      <source src="/video/thick-oak-sharpening-block.mp4">
  </video>
  <figcaption>Presentation of the oak sharpening block</figcaption>
  </figure>

<p>It was a bit too thick though&hellip;</p>
<h2 id="stropping-compounds">Stropping compounds</h2>
<h3 id="the-green-stuff">The green stuff</h3>
<p>Most people get a green bar of compound and use it all their life and never think about it. But nooo, I had to do research and see what that compound contains and why is it so waxy and <em>is there better stuff?</em></p>
<p>My understanding is that the green stuff is made of very fine particles of <strong>chromium oxide</strong>, which is also used as green pigment in cosmetics and painting, and is why it makes everything in your life green when touched.</p>














<a href="https://alinpanaitiu.comimages/dialux-vert.png">
  <figure>
    <figcaption>Dialux Vert green stropping compound</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/dialux-vert.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/dialux-vert.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/dialux-vert.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/dialux-vert.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/dialux-vert.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/dialux-vert.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/dialux-vert.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/dialux-vert.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/dialux-vert.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/dialux-vert.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/dialux-vert.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/dialux-vert.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/dialux-vert.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/dialux-vert.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/dialux-vert.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/dialux-vert.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/dialux-vert.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/dialux-vert.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/dialux-vert.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/dialux-vert.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/dialux-vert.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/dialux-vert.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/dialux-vert.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/dialux-vert.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/dialux-vert.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/dialux-vert.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/dialux-vert.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/dialux-vert.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/dialux-vert.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/dialux-vert.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/dialux-vert.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/dialux-vert.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/dialux-vert.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/dialux-vert.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/dialux-vert.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/dialux-vert.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/dialux-vert.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/dialux-vert.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/dialux-vert.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/dialux-vert.png"
        alt="Dialux Vert green stropping compound"
        
        
        style="max-width: min(300px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>I had a <a href="https://www.osborn.com/en/EUPOSC002~dialux-green"  target="_blank" rel="noopener" >Dialux Vert</a> bar, and searching for its material safety <a href="https://www.thepolishingshop.co.uk/image/catalog/MSDS/Dialux%20green_gb_.pdf"  target="_blank" rel="noopener" >data sheet</a> surfaces the following:</p>
<blockquote>
<p>Composition/information on ingredients:<br>
    mixture of fatty acids and paraffins, aluminum oxide, chromium oxide</p>
</blockquote>
<p>So it is made of very fine particles of chromium and aluminum oxide suspended in paraffin wax.</p>
<p>Does that mean it can be melted and poured into cylindrical molds? Yes it does! Adding some green flakes in a small cup with walnut oil in it allows it to be melted in a microwave oven.</p>
<blockquote>
<p>Any type of oil works, I tested sunflower oil, olive oil, coconut oil, mineral oil etc. you just need some kind of non-solid fat to make the paste less viscous when melted.</p>
</blockquote>
<p>After lining the <code>20mm</code> hole with wax paper and pouring the melted mix, it solidified as a green cylinder with a chapstick-like consistency, that I could easily get out and apply to the cork.</p>
<h3 id="diamonds">Diamonds</h3>
<p>In the last decade, diamond pastes and sprays started becoming popular for stropping.</p>














<a href="https://alinpanaitiu.comimages/diamond-stropping-compounds.png">
  <figure>
    <figcaption>diamond powder, paste and spray</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/diamond-stropping-compounds.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/diamond-stropping-compounds.png"
        alt="diamond powder, paste and spray"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p><a href="https://hitechdiamond.com/products/diamond-powder?variant=33578765811757"  target="_blank" rel="noopener" >Diamond powder</a> was widely used for lapping gemstones for a long time, and <a href="https://hitechdiamond.com/products/diamond-paste?variant=33578750378029"  target="_blank" rel="noopener" >diamond paste</a> was being used in the dental industry for polishing implants. Eventually people figured they can suspend the very fine diamonds in a sprayable emulsion, advertise it for stropping and ask a ton of money for it.</p>
<p><a href="https://www.gritomatic.com/products/gunny-juice-poly-diamond-emulsion?variant=40707239149677"  target="_blank" rel="noopener" >Diamond sprays</a> are not accessible here in Romania, but I was able to get some lapping pastes of <code>3 microns</code> and <code>0.25 microns</code> particle size to test.</p>
<blockquote>
<p>For reference, chromium oxide green polishing compound is usually formulated with <code>0.5 microns</code> particle size, but that doesn’t mean that all particles are of that size.</p>
<p>That’s like the average particle size which gives you an idea of what kind of polish you can expect from it.</p>
</blockquote>
<p>I admit, I like diamonds. They’re easy to apply by squirting the paste from a syringe, I can throw it in a bag without making a green mess, it does seem to cut metal very slightly faster and lasts a bit longer between applications. But a tiny syringe that lasts 3 months costs more than a green bar that lasts me years. I can’t make this compromise.</p>
<p>There are people that bought the diamond powder directly and <a href="https://youtu.be/TVJk1K5fm5M?si=BSGkodNpqSN806ai"  target="_blank" rel="noopener" >made their own</a> pastes and emulsions but it all seems too complicated for little gain.</p>
<p>I did try some cheap <a href="https://www.aliexpress.com/item/1005006308268632.html"  target="_blank" rel="noopener" >Chinese AliExpress diamond pastes</a>, but for the life of me I can’t figure out how they calculate the grit and particle size. I bought the finest I could find which is listed as <code>W0.5 micron mesh</code>. I don’t know what means but it doesn’t get to a shaving sharp blade easily.</p>
<h3 id="cubic-boron-nitride">Cubic boron nitride</h3>
<p>Looking into other abrasives, there’s another loved stropping compound: cubic boron nitride or <a href="https://www.chefknivestogo.com/ckcbnsp1mi.html"  target="_blank" rel="noopener" >CBN emulsions</a>.</p>
<p>It’s impossible to buy that here, but it’s way too expensive anyway and I’m pretty sure the difference would be marginal.</p>
<p>People tout that CBN and diamonds are actually <em>necessary</em> for harder steels, but I&rsquo;ve yet to see that tested. My guess is that it&rsquo;s just people&rsquo;s way of justifying the purchase of a new and expensive toy, we&rsquo;re all guilty of that.</p>
<p><a href="https://en.wikipedia.org/wiki/Chromium%28III%29_oxide#Structure_and_properties"  target="_blank" rel="noopener" >Wikipedia</a> says that <code>Cr₂O₃</code> has a hardness of <code>8 to 8.5 Mohs</code> which is far higher than plain steel at <code>4 Mohs</code> and even higher than tungsten at <code>7.5 Mohs</code>. If it can scratch tungsten, I&rsquo;d say it can hone a steel knife.</p>
<h3 id="corundum">Corundum</h3>
<p>I eventually found a pale pink compound that has the same fine grit as the green one, but without the color problem.</p>














<a href="https://alinpanaitiu.comimages/pink-corundum-brick.jpeg">
  <figure>
    <figcaption>800g brick of pink corundum stropping compound</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/pink-corundum-brick.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/pink-corundum-brick.jpeg"
        alt="800g brick of pink corundum stropping compound"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>It’s made from <a href="https://en.wikipedia.org/wiki/Corundum"  target="_blank" rel="noopener" >corundum</a>, which is aluminum oxide without the chromium. It&rsquo;s harder than the green stuff (at <code>9 Mohs</code>), doesn&rsquo;t leave its color everywhere, and it&rsquo;s dirt cheap. I bought the <a href="https://dtgrinder.com/termek/polishing-paste-maxfin-7-8-lea-light-green-for-ferrous-metals-800-g-masolat/?lang=en"  target="_blank" rel="noopener" >Lea Chromax</a> brand and I’m very happy with it so far. I paid <code>€10</code> for a huge <code>800g</code> brick that will last me a lifetime.</p>
<p>I break small chalk-like pieces from it that I pocket or leave throughout the house to ensure I always hame some on hand. It applies easily to both cork and leather and doesn&rsquo;t flake off or stick to the blade.</p>
<p>I gifted sharpening blocks along with pieces of stropping compound to many friends and relatives and the pink Chromax block still looks as large as when I bought it.</p>
<h2 id="third-iteration">Third iteration</h2>
<p>I am working on a way to merge the <em>all-in-one</em> quality of the thick oak block with the <em>pocketability</em> of the thin beech block.</p>
<p>I also got my hands on some narrow <code>20mm</code> width diamond plates of very fine <code>3000grit</code>. I find them useful for when I need a very sharp blade for doing finishing cuts on a spoon or when planing dense wood. I’d like to integrate this plate somehow in the block.</p>














<a href="https://alinpanaitiu.comimages/diamond-plate-3000.jpeg">
  <figure>
    <figcaption>fine diamond plate of 3000 grit</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/diamond-plate-3000.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/diamond-plate-3000.jpeg"
        alt="fine diamond plate of 3000 grit"
        
        
        style="max-width: min(500px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>It could maybe be glued to the side of a <code>20mm</code> thick block of wood, and used hand held instead of on a table.</p>
<p>I did try that on the beech block I already had, and it seems to work nicely. It&rsquo;s not that hard to sharpen freehand and handheld as long as it&rsquo;s just for doing the last fine honing on an already sharp blade.</p>














<a href="https://alinpanaitiu.comimages/beech-block-3000-diamonds.jpeg">
  <figure>
    <figcaption>beech block with the 3000 grit diamond plate on the side</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/beech-block-3000-diamonds.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/beech-block-3000-diamonds.jpeg"
        alt="beech block with the 3000 grit diamond plate on the side"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>The wax paper method for pouring melted compound is also not ideal. I’m thinking that pouring the compound into a chapstick tube would make it easier to use, and be thinner so it can fit inside a hole in the wood block.</p>
<hr>
<p>I also have these round diamond files which are great for sharpening round blades, serrated knifes, drill bits, even some rip cut saws. They come as a double-ended rod with a conical file on one end, and a round + flat file on the other end, and they&rsquo;re meant to be placed in a pencil-like holder.</p>














<a href="https://alinpanaitiu.comimages/diamond-round-files.jpeg">
  <figure>
    <figcaption>round diamond file, cut in half</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/diamond-round-files.jpeg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/diamond-round-files.jpeg"
        alt="round diamond file, cut in half"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>I cut the rod in half with an angle grinder as I can&rsquo;t fit the whole length of the rod inside the wood block. With only half of it, I can drill a <code>6mm</code> hole in the block to embed the file.</p>
<p>I can&rsquo;t fit the cork lined wooden rod for honing though. I only have <code>3mm</code> thick cork for now which is way too thick for this use case.</p>
<p>There&rsquo;s this thing called <a href="https://www.knivesandtools.com/en/pt/-jende-nanocloth-ultra-strop-1-micron-bench-strop.htm"  target="_blank" rel="noopener" >Nanocloth</a> which is like a thin microfiber cloth specifically for CBN and diamond emulsions. It&rsquo;s hella expensive and I would never buy such a thing, but it gave me an idea: I could try wrapping some thin felt cloth around a <code>10mm</code> diameter wooden dowel. It should hopefully fit in the block and hold enough compound for stropping.</p>
<p>And I guess that&rsquo;s it, that would be my ideal sharpening method:</p>
<ul>
<li>a compact block of solid wood</li>
<li>with 2 wide diamond plates on one face <em>(coarse and fine grit)</em></li>
<li>a cork strop on the other face</li>
<li>a narrow diamond plate on the side <em>(very fine grit)</em></li>
<li>an embedded round diamond file</li>
<li>an embedded strop rod</li>
<li>and a stropping compound, inside the wood block somehow, that&rsquo;s easy to get out and apply</li>
</ul>
<p>And all that in a cheap package that should cost less than $30 to assemble.</p>
<p>I&rsquo;m still working on it, I prepared some new <code>18mm</code> oak blocks and I&rsquo;ll update this post with the results.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>Woodworking as an escape from the absurdity of software</title>
      <link>https://alinpanaitiu.com/blog/woodworking-escape-from-software-absurdity/</link>
      <pubDate>Sun, 28 Apr 2024 13:01:45 +0300</pubDate>
      <author>alin@panaitiu.com (Alin Panaitiu)</author>
      <guid>https://alinpanaitiu.com/blog/woodworking-escape-from-software-absurdity/</guid>
      <description>
        <![CDATA[<p>Some of you might remember the <a href="https://github.com/docker/cli/issues/267#issuecomment-695149477"  target="_blank" rel="noopener" >legendary comment of Eric Diven</a> on a Docker CLI issue he opened years ago:</p>
<blockquote>
<p>@solvaholic: Sorry I missed your comment of many months ago. I no longer build software; I now make furniture out of wood. The hours are long, the pay sucks, and there&rsquo;s always the opportunity to remove my finger with a table saw, but nobody asks me if I can add an RSS feed to a DBMS, so there&rsquo;s that :-)</p>
</blockquote>
<p>I say <em>legendary</em> because it has over 9000 reactions and most are positive. There&rsquo;s a reason why so many devs resonate with that comment.</p>
<p>A lot of us said at some point things like <em>&ldquo;I&rsquo;m gonna throw my laptop out the window and start a farm&rdquo;</em>. Even my last team leader sent me a message out of the blue saying <em>&ldquo;I think I&rsquo;ll run a bar. I want to be a bartender and listen to other people&rsquo;s stories, not figure out why protobuf doesn&rsquo;t deserialize data that worked JUST FINE for the past three years&rdquo;</em>.</p>
<p>You know the drill, sometimes the world of software development feels so absurd that you just want to buy a hundred alpaca and sell some wool socks and forget about solving conflicts in <code>package.json</code> for the rest of your life.</p>
<p>I went through those stages too: when the Agile meetings at my last job got so absurd that we were being asked to estimate JIRA task time in T-shirt sizes, I took the decision to quit that comfy well paying job for the uncertainty of making a living from macOS apps. <em>I had only one app that didn&rsquo;t even work on the latest Apple Silicon chips, and it was making $0, so I really took a bet with it.</em></p>
<p>Recently, when people started coming with so many unrealistic and absurd expectations and demands about what my apps should do, I started thinking if it would be possible to leave software development for a more physical trade.</p>
<h2 id="a-bit-of-history">A bit of history</h2>
<p>Most of my pre-college time was spent on things I didn&rsquo;t want to do.</p>
<p>I had a bit of childhood, but then I started going to school 6 hours per day, with 1-2 hours spent on commute after 5th grade. I only liked the 10-minute breaks between classes where I played basketball or practiced parkour.</p>
<p>Every day after I came back from school, I had to work in agriculture, either out in the field with crazy winds and sun and UV radiation, or inside a 100-meter long greenhouse where it&rsquo;s either a 50°C sauna or a muddy rainforest. I was very bad at every job I was given, but it&rsquo;s what my parents did for a living and I had to help them, no questions asked.</p>
<p>The few hours that remained, usually very late at night, tired both physically and mentally, I spent practicing acoustic guitar, doing bodybuilding exercises, writing bad poetry or drawing graphite portraits.</p>














<a href="https://alinpanaitiu.comimages/guitar-alin-classical-slippers.jpg">
  <figure>
    <figcaption>me, ages ago, playing a classical guitar on someone&#39;s old couch</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 350px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 350px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 350px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/guitar-alin-classical-slippers.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/guitar-alin-classical-slippers.jpg"
        alt="me, ages ago, playing a classical guitar on someone&#39;s old couch"
        
        
        style="max-width: min(350px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>I almost never did homework or memorize whatever had to be memorized for the next day of school. I just couldn&rsquo;t justify spending those few hours I had left on even more stuff I did not want to do.</p>
<p>When I found my liberty in college, hundreds of kilometers away from my parents, it&rsquo;s like something clicked. I suddenly became incapable of doing work that I found meaningless.</p>
<p>Failing classes became acceptable, quitting jobs was something I did with little remorse if I felt I wasn&rsquo;t helping anyone with the work I was assigned, and bureaucracy became a disease I had to avoid at all costs.</p>
<p><em>I still washed the dishes though. Cleaning and other &ldquo;chores&rdquo; never felt meaningless for some reason.</em></p>
<h2 id="the-first-wood-thing-i-did">The first wood thing I did</h2>
<p>… was a chess board and piece set. With magnets inside them. Where the pieces look nothing like ordinary chess pieces.</p>














<a href="https://alinpanaitiu.comimages/chess-board-on-carpet.jpg">
  <figure>
    <figcaption>chess board, first iteration</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/chess-board-on-carpet.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/chess-board-on-carpet.jpg"
        alt="chess board, first iteration"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>I was trying to get the pieces to snap into place in a satisfying way, and make sure the game stays that way when kids or dogs inevitably bump the table where the board sits.</p>
<p>You know how Magnus Carlsen always adjusts his pieces so meticulously before a game? Well I have half of that obsession as well so I wanted to avoid doing that.</p>


<div style="width: 100%; display: flex; justify-content: center">
  <figure style="width: 100%;">
    <video disablepictureinpicture autoplay loop muted playsinline disablepictureinpicture style="width: 90%; margin: 10px auto; border-radius: 8px;"  >
      <source src="/video/magnus-carlsen-adjusting-pieces.mp4">
  </video>
  <figcaption>Magnus Carlsen adjusting his pieces before a game</figcaption>
  </figure>

  <figure style="width: 100%;">
    <video autoplay loop muted playsinline disablepictureinpicture style="width: 90%; margin: 10px auto; border-radius: 8px;"  >
      <source src="/video/chess-piece-snapping-in-place.mp4">
  </video>
  <figcaption>pawn snapping into its square because of the magnet inside</figcaption>
  </figure>
</div>

<hr>
<h3 id="how-it-was-done">How it was done</h3>
<p>I started with a cheap but hefty pine board which I rounded with a lot of sandpaper. Then I asked my wife to help me colour in the darker squares because I&rsquo;m pretty bad at colouring inside the edges <em>(both literally and figuratively)</em>. We used some wood floor markers for that and the colour seems to be holding well.</p>
<blockquote>
<p>Most chess board builds you see on YouTube are done by gluing squares of different wood species with alternating colors, but I had neither the skill nor the tools to do that.</p>
</blockquote>
<p>Then I drilled holes for the super strong neodymium magnets from the underside of the board, having to get really close to the top side without passing through. I failed on two squares, but some wood putty took care of that.</p>














<a href="https://alinpanaitiu.comimages/sculpting-chess-pieces-balcony.jpg">
  <figure>
    <figcaption>sculpting chess pieces with my dremel on the balcony</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sculpting-chess-pieces-balcony.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/sculpting-chess-pieces-balcony.jpg"
        alt="sculpting chess pieces with my dremel on the balcony"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>I spent a few sunny days on the balcony sculpting the pieces with a badly sharpened knife and my Dremel. This was quite satisfying, there&rsquo;s something really nice about seeing a non-descript rectangle take the shape of a little horse in your hands. I mean <a href="https://en.wikipedia.org/wiki/Knight_%28chess%29"  target="_blank" rel="noopener" >knight</a>, but in Romanian that piece is called &ldquo;horse&rdquo;, and I really don&rsquo;t see any knight there.</p>














<a href="https://alinpanaitiu.comimages/chess-board-collage.jpg">
  <figure>
    <figcaption>chess board, start to finish</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/chess-board-collage.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/chess-board-collage.jpg"
        alt="chess board, start to finish"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>Regarding the design, I got some inspiration after seeing these <a href="http://www.chess-museum.com/modern-and-modernist---special-show.html"  target="_blank" rel="noopener" >modernist chess sets</a>, which not only looked beautiful in my eyes, but also had these geometric shapes that didn&rsquo;t need that much sculpting to replicate. I found ready-to-buy spheres and cubes of wood at a craft shop around me <em>(which took care of pawns and rooks)</em>, and the rest were carved out of rectangles and cones of wood.</p>














<a href="https://alinpanaitiu.comimages/modernist-chess-sets.jpg">
  <figure>
    <figcaption>Modernist chess set designs</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/modernist-chess-sets.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/modernist-chess-sets.jpg"
        alt="Modernist chess set designs"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<h2 id="kaval">Kaval</h2>
<p>Two Octobers ago, a Romanian music band called <a href="https://www.youtube.com/watch?v=_OkPq67c3ik"  target="_blank" rel="noopener" >Subcarpați</a> was holding a free <em>&ldquo;make a Kaval with your own hands&rdquo;</em> course, where a flute artisan taught the basics of his trade for a week.</p>
<blockquote>
<p>The Kaval or &ldquo;caval&rdquo; is a long flute with 5 holes and a distinct lower register where notes can sound melancholic and coming from far away, as opposed to the thin cheerful sound of the small shepherd flute.</p>
</blockquote>


<figure style="width: min-content">
  <audio style="border-radius: 6px" controls src="/audio/a-lupilor.mp3"></audio>
  <figcaption>Kaval sample in G minor</figcaption>
</figure>

<p>Ever since I bought my first Kaval, I always wanted to learn how to build one myself. It&rsquo;s one of those trades where there&rsquo;s very little info on the internet, so it feels almost mystical compared to what I&rsquo;m used to in programming. I would also have the chance to walk home with the finished flute, so of course I went to the course.</p>














<a href="https://alinpanaitiu.comimages/making-caval-bminor.jpg">
  <figure>
    <figcaption>Making my own Kaval, in B minor</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/making-caval-bminor.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/making-caval-bminor.jpg"
        alt="Making my own Kaval, in B minor"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>I loved the fact that we worked in teams of two, and that everything had to be done by hand with no power tools. Even the long bore through the 70cm branch of elder tree had to be done with a hand drill, taking turns to rest our hands.</p>
<p>The artisan had been a shepherd himself since childhood, and taught himself with a lot of trial and error about how to build good sounding flutes and how to make the holes so that the flute stays in tune. But he didn&rsquo;t know <em>why</em> the holes should be at those specific distances or why the wood tube should be of that specific length for each scale.</p>
<p>I wanted to know those things, because I had an idea of making a universal Kaval that can play in any scale.</p>
<blockquote>
<p>You see, if you want to play on top of songs in various scales, you need a Kaval made for each specific scale. So you&rsquo;ll need an A minor flute, and a B minor one and a C minor one and so on, for a total of 12 different flute lengths.</p>
</blockquote>
<p>I eventually found info on <a href="https://newt.phys.unsw.edu.au/jw/fluteacoustics.html"  target="_blank" rel="noopener" >how a flute works</a> by thinking about it as an open or closed tube where the vibrating air creates nodes and antinodes that should coincide with the hole position. At the moment I&rsquo;m still studying this and working towards my &ldquo;universal flute&rdquo; goal.</p>
<h2 id="what-does-this-have-to-do-with-software">What does this have to do with software?</h2>
<p>For the past 10 years I lived in rented apartments, usually at the 3rd or 4th story with no access to a courtyard. I was never able to get used to that, given that all my childhood I lived and played in a 2000m² courtyard, on a road where there were more slow horse carriages than noisy cars.</p>
<p>This year I moved into a rented house with a tiny but welcoming garden and a bit of paved court and only now I notice the effect this has had on my mind and behaviour.</p>
<p>I develop <a href="https://lowtechguys.com/"  target="_blank" rel="noopener" >macOS apps</a> for a living, and there are some unhealthy things in this field that piled up over the years. I get a lot of messages in a demanding and negative tone, and because walking outside the apartment meant unbearable car noise, obnoxious smells and zero privacy, I always defaulted to simply acting on the feedback, putting up with it and working long hours into the night, instead of going for a walk to calm down.</p>
<p>A few months ago, the most absurd demands started coming up for my apps: things like <em>&ldquo;why does your app not control the volume of my &lt;weird sound device&gt;? why don&rsquo;t you just do it, people pay you for it&rdquo;</em> when the app in question is <a href="https://lunar.fyi/"  target="_blank" rel="noopener" >Lunar</a>, an app for controlling <strong>monitor brightness</strong>, not sound devices.</p>
<p>Or <em>&ldquo;why do you disable your apps from working on Windows?&rdquo;</em>, or <em>&ldquo;make Clop compress text and copy it to clipboard&rdquo;</em> (where <a href="https://lowtechguys.com/clop"  target="_blank" rel="noopener" >Clop</a> is my app that automatically compresses copied images, videos and PDFs, I have no idea what compressing text even means in that context).</p>
<p>But this time, I was able to simply walk out the front door, grab a branch of beech wood, and, because I remembered my wife saying we forgot to package the french rolling pin when moving, I took out my pocket knife and started carving a simple rolling pin for her. It was so liberating to be able to just ignore those messages for a while and do something with my hands.</p>














<a href="https://alinpanaitiu.comimages/beech-rolling-pin.jpg">
  <figure>
    <figcaption>the rolling pin is such a simple tool and to this day, my wife still tells me how much she likes it because it&#39;s exactly the right length and thickness for making her tasty egg noodles.. and best of all, it was free</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/beech-rolling-pin.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/beech-rolling-pin.jpg"
        alt="the rolling pin is such a simple tool and to this day, my wife still tells me how much she likes it because it&#39;s exactly the right length and thickness for making her tasty egg noodles.. and best of all, it was free"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>I understand that those people don&rsquo;t know better, and they would have no idea that there&rsquo;s no checkbox where you can choose whether an app works on macOS, Windows or Linux. I understand how if the app does <em>something</em> with audio volume or compression, some think that it should do <em>everything</em> related to those workloads, even if it&rsquo;s completely outside the scope of the app.</p>
<p>But the combination of the <strong>negative tone</strong> and <strong>getting message after message</strong>, some people being so persistent that they insist on sending me those messages through all possible mediums <em>(email, Discord, Twitter, contact form, they&rsquo;ll find me everywhere)</em>, makes it hard to just ignore them.</p>
<p>There&rsquo;s also this oily smell of AI and machine learning in the tech atmosphere, where I no longer feel relevant and I seem to have stopped caring about new tech when I noticed that 8 in 10 articles are about some new LLM or image generation model. I guess I like the smell of wood better.</p>
<h3 id="side-tangent-on-privileges-of-being-a-software-dev">Side tangent on privileges of being a software dev</h3>
<p>I know I&rsquo;m privileged to even be able to have the choice of what to do with my time. I got lucky when I chose a computer science university at the right time which allowed me to progress towards a huge semi-passive income in the last 10 years. <em>that doesn&rsquo;t mean I didn&rsquo;t work my ass off, but luck plays a huge role too</em></p>
<p>I got <em>&ldquo;lucky&rdquo;</em> to have my mind traumatised into some kind of OCD-like state where I hate leaving a thing unfinished. So I plow through exhaustion, skip meals, miss house chores and annoy dear people around me because I know <strong>&ldquo;I just need to fix this little thing&rdquo;</strong> and I&rsquo;ll finish this app/feature/task I started. Even though I also know there&rsquo;s no real deadline and I can leave it half-finished for now.</p>
<p>But even if it sounds annoying for a person like me to whine about how I don&rsquo;t feel good or I feel burnt out, the privilege doesn&rsquo;t negate the feelings. The regression to the norm will make everyone, rich or poor, get used to the status quo and complain about every thing that&rsquo;s just a little worse than their current state. That&rsquo;s happiness and sadness in a nutshell.</p>
<p>I&rsquo;m also vaguely aware that software dev as we know it is about to disappear soon, and I got tired of learning the newest thing just to have it replaced next year. I got tired of back pain and chronic finger pain from so many hours of sitting and typing, I&rsquo;d rather have pain from work that also builds some muscle.</p>
<p>And I got so tired of everything being online, immaterial, ephemeral and lonely, like indie development tends to be.</p>
<hr>
<h2 id="woodworking-with-cheap-tools-and-free-wood">Woodworking with cheap tools and free wood</h2>
<p>This house we rented is small and the owners had to fit the bedroom upstairs. I really don&rsquo;t like climbing stairs up and down, especially when I have to let my dog out three times per night. So we gave up a room and started furnishing our own bedroom downstairs.</p>
<p>I didn&rsquo;t want to buy bedside tables for the price of the bed itself, so I thought I could maybe make by own. <em>I&rsquo;m not yet skilled enough to build my own bed though, so we bought that</em></p>
<p>One day, while walking with my dog, I noticed that some trees were getting trimmed in the vicinity of our house and there were a lot of white birch branches on the side of the road. I said <strong>why not?</strong>, grabbed some branches and walked like a lunatic with white long sticks dangling up and down and a black dog zig-zagging left and right, all the way home.</p>
<p>I had another small pine panel left from that chess project so I started thinking about the simplest way to turn what I have into a bedside table.</p>














<a href="https://alinpanaitiu.comimages/bedside-table-before.jpg">
  <figure>
    <figcaption>pine board with birch branches</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 600px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 600px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 600px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bedside-table-before.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/bedside-table-before.jpg"
        alt="pine board with birch branches"
        
        
        style="max-width: min(600px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>I used low-grit sandpaper to give the board some nice round corners because I love <a href="https://webflow.com/blog/squircle-vs-rounded-squares"  target="_blank" rel="noopener" >squircles</a>, <em>swallowed about a spoonful of sawdust because I couldn&rsquo;t find any breathing mask left</em>, criss-crossed 4 branches in a way that would give a stable base, and screwed them to the underside of the board with long wood screws.</p>
<p>The legs would wobble around though, so I drilled small 3mm holes into each branch where they met in the middle, and weaved a florist wire through them to keep the table steady.</p>














<a href="https://alinpanaitiu.comimages/bedside-table.jpg">
  <figure>
    <figcaption>Bedside table, made out of pine with birch legs</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 450px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bedside-table.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bedside-table.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bedside-table.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bedside-table.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bedside-table.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bedside-table.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bedside-table.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bedside-table.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bedside-table.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bedside-table.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bedside-table.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bedside-table.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bedside-table.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 450px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bedside-table.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bedside-table.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bedside-table.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bedside-table.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bedside-table.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bedside-table.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bedside-table.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bedside-table.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bedside-table.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bedside-table.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bedside-table.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bedside-table.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bedside-table.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 450px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bedside-table.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bedside-table.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bedside-table.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bedside-table.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bedside-table.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bedside-table.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bedside-table.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bedside-table.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bedside-table.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bedside-table.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bedside-table.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bedside-table.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bedside-table.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/bedside-table.jpg"
        alt="Bedside table, made out of pine with birch legs"
        
        
        style="max-width: min(450px, 80vw)"
      >
    </picture>
  </figure>
</a>

<h3 id="the-laptop-bed-table">The laptop bed table</h3>
<p>After I&rsquo;ve shown the bedside table to a friend of mine, he said he also needed a laptop table for those mornings when he&rsquo;d rather not get out of bed. I wanted to say <em>that&rsquo;s not very healthy</em>, but what got out instead was <em>sure thing, I&rsquo;ll do it!</em>. Oh well..</p>
<p>I still had the large desk top I glued from smaller beech boards, on which I worked for the past 4 years. It stayed unused currently, so I cut part of it and built this cute thing:</p>














<a href="https://alinpanaitiu.comimages/masa-danut.jpg">
  <figure>
    <figcaption>cute but heavy laptop table, made out of glued beech wood</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/masa-danut.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/masa-danut.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/masa-danut.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/masa-danut.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/masa-danut.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/masa-danut.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/masa-danut.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/masa-danut.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/masa-danut.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/masa-danut.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/masa-danut.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/masa-danut.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/masa-danut.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/masa-danut.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/masa-danut.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/masa-danut.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/masa-danut.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/masa-danut.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/masa-danut.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/masa-danut.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/masa-danut.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/masa-danut.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/masa-danut.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/masa-danut.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/masa-danut.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/masa-danut.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/masa-danut.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/masa-danut.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/masa-danut.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/masa-danut.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/masa-danut.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/masa-danut.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/masa-danut.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/masa-danut.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/masa-danut.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/masa-danut.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/masa-danut.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/masa-danut.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/masa-danut.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/masa-danut.jpg"
        alt="cute but heavy laptop table, made out of glued beech wood"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>You&rsquo;ll notice three defining features that every laptop table should have:</p>
<ul>
<li>a hole for a charging cable</li>
<li>a carved coaster for the coffee cup</li>
<li>a mildly surprised face? 😦</li>
</ul>
<p>To tell the truth, all those are side effects of me drilling holes where there should be no hole, and dropping the board on the ground multiple times because my workbench was not large enough. All the things that could go wrong, went wrong with this table.</p>
<p>I hid the defects by turning them into features.</p>
<p>The whole truth actually is that the table looks nothing like what I planned. I bought these nice hidden brass cylindrical hinges to make the table foldable. That way, you could fold the sides flat inside and use it as some kind of armchair desk if you wanted.</p>














<a href="https://alinpanaitiu.comimages/brass-cylindrical-hinges.jpg">
  <figure>
    <figcaption>Brass hinges</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 250px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 250px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 250px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/brass-cylindrical-hinges.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/brass-cylindrical-hinges.jpg"
        alt="Brass hinges"
        
        
        style="max-width: min(250px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>I wasn&rsquo;t able to drill the correctly sized or positioned holes for the hinges because I still lack a lot of knowledge and skill in working with wood. So after losing my temper with the frickin&rsquo; hinges that still didn&rsquo;t fit after a full day of drilling and chiseling, I glued the sides and inserted 2 trusty long wood screws per side, which I patched with a glue gun that made the screw holes look like eyes.</p>
<p>After I also carved the handles, the table grew kind of a personality of its own, as you can see above.</p>
<hr>
<p><strong>Why didn&rsquo;t I do some wood joints, like a dovetail instead of ugly screws and glue?</strong></p>
<p>Because I had no idea they existed. Also, I wasn&rsquo;t able to fit a simple hinge, I would have probably never finished this table if I tried learning wood joinery on it.</p>
<p>This reminds me of how whenever I did pair programming with a colleague, I noticed how they were doing some <em>&ldquo;nonoptimal&rdquo;</em> action and I would say:</p>
<blockquote>
<p>Why don&rsquo;t you just use <code>ripgrep</code> instead of sifting through all these files?</p>
</blockquote>
<p>Because they don&rsquo;t know it exists, stupid. Or because they just want to get this thing done and move on, they don&rsquo;t <code>grep</code> files all day like you do.</p>
<p>But in my ignorance, I seem to have chosen a good enough joining method. As you can see in this <a href="https://www.youtube.com/watch?v=OWFyBCkwLOQ"  target="_blank" rel="noopener" >wood joinery comparison</a>, 5cm (2inch) screws can hold more than 50kg (110lbs) of force, and I used even longer screws so I think it&rsquo;s going to hold a 3kg laptop just fine.</p>
<hr>
<p>Oh right, forgot about this little detail.. I also added a cork pocket for holding a notebook, tablet, phone etc. which I lined with a microfiber cloth on the inside for strength and sewn to the wood with that leftover alpaca wool for style.</p>














<a href="https://alinpanaitiu.comimages/masa-danut-pocket.jpg">
  <figure>
    <figcaption>Cork pocket sewn to the table side</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 350px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 350px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 350px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/masa-danut-pocket.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/masa-danut-pocket.jpg"
        alt="Cork pocket sewn to the table side"
        
        
        style="max-width: min(350px, 80vw)"
      >
    </picture>
  </figure>
</a>

<h3 id="the-bookshelf-without-books">The bookshelf without books</h3>














<a href="https://alinpanaitiu.comimages/bookshelf.jpg">
  <figure>
    <figcaption>Large bookshelf (200x120x40 cm), made out of pine boards</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 450px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bookshelf.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bookshelf.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bookshelf.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bookshelf.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bookshelf.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bookshelf.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bookshelf.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bookshelf.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bookshelf.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bookshelf.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bookshelf.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bookshelf.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bookshelf.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 450px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bookshelf.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bookshelf.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bookshelf.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bookshelf.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bookshelf.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bookshelf.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bookshelf.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bookshelf.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bookshelf.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bookshelf.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bookshelf.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bookshelf.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bookshelf.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 450px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bookshelf.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bookshelf.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bookshelf.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bookshelf.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bookshelf.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bookshelf.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bookshelf.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bookshelf.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bookshelf.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bookshelf.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bookshelf.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bookshelf.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bookshelf.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/bookshelf.jpg"
        alt="Large bookshelf (200x120x40 cm), made out of pine boards"
        
        
        style="max-width: min(450px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>While we were stuck in the apartment in the 2020 pandemic, me and my wife bought a lot of stuff that we thought would help us learn new things and start new hobbies. <em>I thought I&rsquo;m going to build smart LED lighting all my life and my wife would become a professional wool knitter.</em> We were losing our minds, for sure.</p>
<p>So now we were stuck with crates of stuff we haven&rsquo;t used in years, and didn&rsquo;t want to start unpacking them around the house. The clutter that followed after the pandemic, tired our minds just as much as the lockdown itself.</p>
<p>We dumped the crates on an unused stairway spot, and I thought that a bookshelf as large as that spot would clear the clutter.</p>














<a href="https://alinpanaitiu.comimages/bookshelf-before-after.jpg">
  <figure>
    <figcaption>Before: clutter | After: organized clutter</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bookshelf-before-after.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/bookshelf-before-after.jpg"
        alt="Before: clutter | After: organized clutter"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>But I could not find any bookshelf that large, certainly not for cheap. So I traced a few lines in <a href="https://apps.apple.com/us/app/freeform/id6443742539"  target="_blank" rel="noopener" >Freeform</a>, took some measurements, and ordered a bunch of large pine boards and a ton of long screws.</p>
<p><em>I also ordered the cheapest portable workbench I could find ($30) that had a vise, so I can stop making sawdust inside.</em></p>
<p>A few days later, I got to sawing the shelves with my cheap Japanese pull saw I bought from Lidl years ago.</p>
<blockquote>
<p>Hint: Hand sawing a long wood board with no skill will certainly end up with a crooked edge. Stacking up 5 boards one on top of the other will still end up crooked.</p>
</blockquote>
<blockquote>
<p>Uhm, I guess the hint is, buy a track saw, or make sure the crooked edge isn&rsquo;t visible.</p>
</blockquote>














<a href="https://alinpanaitiu.comimages/bookshelf-work-process.jpg">
  <figure>
    <figcaption>making the bookshelf</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bookshelf-work-process.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/bookshelf-work-process.jpg"
        alt="making the bookshelf"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>My wife helped a lot with measuring and figuring out where to drill holes and place the screws, while my dog inspected the work regularly to make sure the defects were hidden correctly.</p>
<p>It took two days of screwing.. erm.. driving screws, I mean. But in the end we got the result we wanted! <em>And I got sores in my right arm for days, driving those long screws is harder than I thought.</em></p>
<h2 id="the-desk-that-became-a-workbench">The desk that became a workbench</h2>
<p>In the thumbnail of this post you can see the current &ldquo;workbench&rdquo; I use, which is basically that $30 vise workbench I bought for the bookshelf, with the top of my previous &ldquo;coding desk&rdquo; attached in the front.</p>














<a href="https://alinpanaitiu.comimages/workbench.jpg">
  <figure>
    <figcaption>my current workbench</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/workbench.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/workbench.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/workbench.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/workbench.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/workbench.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/workbench.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/workbench.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/workbench.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/workbench.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/workbench.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/workbench.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/workbench.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/workbench.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/workbench.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/workbench.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/workbench.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/workbench.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/workbench.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/workbench.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/workbench.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/workbench.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/workbench.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/workbench.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/workbench.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/workbench.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/workbench.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/workbench.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/workbench.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/workbench.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/workbench.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/workbench.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/workbench.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/workbench.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/workbench.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/workbench.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/workbench.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/workbench.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/workbench.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/workbench.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/workbench.jpg"
        alt="my current workbench"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>In the image you can see (bottom-left to top, then right):</p>
<ul>
<li>the cheapest block plane I could find ($8)</li>
<li>a red no-name plane I found in the shed of that 100-year old house that we never finished rebuilding</li>
<li>an axe I found rusted and partly broken in the same shed, on which I learnt how to sharpen and restore axes</li>
<li>a folding japanese pull saw that I take everywhere with me</li>
<li>some grip blocks on which I place boards for sanding</li>
<li>a bottle of Osmo Polyx oil I use for finishing</li>
<li>a set of carving knives from Beavercraft (really good and they were available at a nice discount)</li>
<li>a combination square (tucked somewhere at the top of the bench)</li>
<li>a branch of elder tree, which is prepared for drilling a hole through it for making a kaval</li>
</ul>
<p>I also own some no-name chisels that work well enough and some card scrapers that I still struggle sharpening.</p>
<p>The only power tools I have are a <a href="https://www.makitauk.com/product/ddf487.html"  target="_blank" rel="noopener" >Makita drill</a> and a <a href="https://www.makitauk.com/product/dbo180.html"  target="_blank" rel="noopener" >random orbit sander</a> on which I did spend some money, an old circular saw I found in that old shed <em>(it was good enough to cut miters on that laptop table)</em> and a Dremel I use rarely because I don&rsquo;t like its power cord. I prefer battery powered tools.</p>
<h2 id="the-window-bench">The window bench</h2>
<p>Our dog Cora loves sitting at the window, growling at old people and barking at children passing around. Yeah, she&rsquo;s terrified of children for some reason.</p>
<p>But the window sill is not wide enough and her leg kept falling with a <em>&ldquo;clang&rdquo;</em> on the radiator below. So I widened it by placing two glued up boards of pine on top of the radiator, that I planed and smoothed beforehand.<br>


<div style="width: 100%; display: flex; justify-content: space-around">















<a href="https://alinpanaitiu.comimages/cora-at-window-no-bench.jpg">
  <figure>
    <figcaption>Cora sitting at the window</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 380px, 40vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 380px, 40vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 380px, 40vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/cora-at-window-no-bench.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/cora-at-window-no-bench.jpg"
        alt="Cora sitting at the window"
        
        
        style="max-width: min(380px, 40vw)"
      >
    </picture>
  </figure>
</a>
















<a href="https://alinpanaitiu.comimages/cora-at-window.jpg">
  <figure>
    <figcaption>Cora at the window, with the widened sill</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 380px, 40vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 380px, 40vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/cora-at-window.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 380px, 40vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/cora-at-window.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/cora-at-window.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/cora-at-window.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/cora-at-window.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/cora-at-window.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/cora-at-window.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/cora-at-window.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/cora-at-window.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/cora-at-window.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/cora-at-window.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/cora-at-window.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/cora-at-window.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/cora-at-window.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/cora-at-window.jpg"
        alt="Cora at the window, with the widened sill"
        
        
        style="max-width: min(380px, 40vw)"
      >
    </picture>
  </figure>
</a>


</div>
</p>
<blockquote>
<p>This is when I learned that a hand plane is not some antique tool that nobody uses anymore, but a quite versatile piece that can easily smoothen grain where I would waste 5 sheets of sandpaper and choke on sawdust.</p>
</blockquote>
<p>I had to still let the heat radiate somehow, so I drilled large holes with a <a href="https://sawsonskates.com/forstner-bit/"  target="_blank" rel="noopener" >forstner bit</a>, but I also blew the grain fibers on the underside because I had no idea of this possible problem. Turns out there is a simple solution to drilling large holes without ripping the fibers:</p>
<ol>
<li>Drill a small 3-6mm hole in the center with a normal wood drill, all the way to the other side <em>(this will help see where the forstner bit should be place from both sides)</em></li>
<li>Place the forstner bit in the hole <em>(this also helps with keeping the bit centered)</em> and drill the large hole, stopping midway through the board</li>
<li>Turn the board around and repeat step 2 until you meet the other end of the hole</li>
</ol>
<p>We also wanted to sit with Cora and there was not much space between the bed and the radiator, so I built a narrow bench. I used another two pine boards of the same size, but this time glued them on the side to create a wider board.</p>
<p>For the legs, well the tree trimming continued near us, so one day I found some thick cherry branches which I brought home, scraped the bark from them, then attached them to the bench using screws from the top side.</p>
<p>I was ok with a <em>rustic</em> look so I didn&rsquo;t spend much on finishing, patching holes, or even proper wood drying. I did use the hand plane to chamfer the edges though, I love taking those thin continuous wood shavings from the edge.</p>














<a href="https://alinpanaitiu.comimages/window-bench.jpg">
  <figure>
    <figcaption>Window bench, in the morning sun</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/window-bench.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/window-bench.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/window-bench.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/window-bench.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/window-bench.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/window-bench.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/window-bench.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/window-bench.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/window-bench.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/window-bench.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/window-bench.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/window-bench.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/window-bench.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/window-bench.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/window-bench.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/window-bench.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/window-bench.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/window-bench.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/window-bench.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/window-bench.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/window-bench.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/window-bench.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/window-bench.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/window-bench.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/window-bench.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/window-bench.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/window-bench.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/window-bench.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/window-bench.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/window-bench.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/window-bench.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/window-bench.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/window-bench.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/window-bench.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/window-bench.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/window-bench.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/window-bench.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/window-bench.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/window-bench.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/window-bench.jpg"
        alt="Window bench, in the morning sun"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<h2 id="the-trunk-coffee-table">The trunk coffee table</h2>














<a href="https://alinpanaitiu.comimages/trunk-table.jpg">
  <figure>
    <figcaption>Coffee table made out of a beech log</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 600px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/trunk-table.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/trunk-table.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/trunk-table.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/trunk-table.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/trunk-table.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/trunk-table.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/trunk-table.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/trunk-table.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/trunk-table.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/trunk-table.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/trunk-table.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/trunk-table.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/trunk-table.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 600px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/trunk-table.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/trunk-table.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/trunk-table.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/trunk-table.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/trunk-table.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/trunk-table.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/trunk-table.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/trunk-table.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/trunk-table.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/trunk-table.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/trunk-table.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/trunk-table.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/trunk-table.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 600px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/trunk-table.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/trunk-table.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/trunk-table.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/trunk-table.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/trunk-table.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/trunk-table.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/trunk-table.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/trunk-table.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/trunk-table.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/trunk-table.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/trunk-table.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/trunk-table.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/trunk-table.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/trunk-table.jpg"
        alt="Coffee table made out of a beech log"
        
        
        style="max-width: min(600px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>We recently visited my parents, and loved how the grass finally started growing in some spots where their house and court renovation was finished and was no longer spewing cement dust. It was an abnormally sunny April and I wanted to chat with them at a coffee outside in the early morning before they started the field work, but there was nowhere to place the coffee outside.</p>
<blockquote>
<p>First world problems right? If you read about <a href="https://waitbutwhy.com/2015/12/the-tail-end.html"  target="_blank" rel="noopener" >The tail end</a>, you might already understand why a trivial thing like coffee time with my parents feels so important to me.</p>
</blockquote>
<p>So one day, while walking on a gravel road near their house, I noticed one neighbour had these huge logs of beech that were recently cut. I thought that would be easy to turn into a small exterior coffee table, so I went to ask if I could buy one.</p>
<p>Well I kind of had to yell <em>&ldquo;HELLO!&rdquo;</em> at their gate because I didn&rsquo;t know their name, and did that a few times until a seemingly sleepy old man appeared at the front door <em>(it was 5 PM)</em> asking what I want. I asked how much he&rsquo;d want for one of those logs, but he just said to get one, no money needed. Ok, there&rsquo;s no point in insisting, I chose a wide enough but not too wide log, because these things are heavy and I wasn&rsquo;t sure I could lift it, and rolled it slowly back home.</p>
<p>I didn&rsquo;t have my usual tools at my parents house, so I improvised. I found a battered cleaver which my dad used for chopping kindling for the barbecue. I sharpened it as well as I could, then used a hammer to roll a burr on the back of the cleaver that I could use for scraping.</p>
<p>Beech wood has such a smooth hard wood under the bark that it didn&rsquo;t even need sanding. I used my dad&rsquo;s power planer to smooth out the top and make a quasi-flat surface then finished it with some walnut oil and it was (almost)  ready!</p>
<p>Because the wood was so green, it was certain that it will crack and roughen as it dried. So I cut a groove and wrapped a flat iron band around the top to keep it from moving too much. The bottom can expand as much as it wants, I&rsquo;m actually quite curious to watch the table morph throughout the summer as we use it.</p>
<h2 id="the-orchard-bench">The orchard bench</h2>














<a href="https://alinpanaitiu.comimages/garden-bench.jpg">
  <figure>
    <figcaption>Bench made from reclaimed wood, for my parents-in-law orchard</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/garden-bench.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/garden-bench.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/garden-bench.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/garden-bench.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/garden-bench.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/garden-bench.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/garden-bench.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/garden-bench.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/garden-bench.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/garden-bench.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/garden-bench.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/garden-bench.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/garden-bench.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 700px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/garden-bench.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/garden-bench.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/garden-bench.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/garden-bench.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/garden-bench.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/garden-bench.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/garden-bench.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/garden-bench.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/garden-bench.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/garden-bench.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/garden-bench.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/garden-bench.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/garden-bench.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 700px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/garden-bench.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/garden-bench.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/garden-bench.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/garden-bench.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/garden-bench.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/garden-bench.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/garden-bench.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/garden-bench.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/garden-bench.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/garden-bench.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/garden-bench.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/garden-bench.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/garden-bench.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/garden-bench.jpg"
        alt="Bench made from reclaimed wood, for my parents-in-law orchard"
        
        
        style="max-width: min(700px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>Because we were born in villages that aren&rsquo;t that far apart, me and my wife always visit both our parents in the same trip. This time when I got to my parents-in-law, I took a stroll through their little orchard. They added new trees this year! I can&rsquo;t wait to taste the large apricots.</p>
<p>What struck me as odd about the orchard was that there was no patch of grass to lay on. They like digging up the soil every year, and leaving it like that: an arid looking patch of land made of dry dirt boulders. I thought a bench would be a good solution and what do you know, there was an old broken door thrown in the firewood pile just outside the orchard, that had the perfect length and width for a bench.<br>
I forgot to take a photo of the door, but it looked kind of like this one, only worse and with a large rhomboid ◊ hole at the top.</p>














<a href="https://alinpanaitiu.comimages/old-broken-wood-door.jpg">
  <figure>
    <figcaption>old broken wooden door</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 300px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 300px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 300px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/old-broken-wood-door.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/old-broken-wood-door.jpg"
        alt="old broken wooden door"
        
        
        style="max-width: min(300px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>I got to work immediately, dismantling the door piece by piece and pulling out nail after nail <em>(they really liked their nails in those old times)</em>. I was left with two long and narrow wooden boards, a pile of rotten wood and two pocketfuls of rusted nails.</p>
<p>I sawed the broken ends of the boards, then I used my father-in-law&rsquo;s power planer to remove the old gray wood from the top, bottom and sides to get to the fresh wood below. <em>There were a lot of holes and valleys so I had to scrape them by hand with sandpaper rolled around a screwdriver.</em> This took a few more days than I expected, but I eventually got two cleanish boards of.. fir? pine? No idea.</p>
<p>I used a velcro sandpaper attachment for the battery powered drill to sand out the rotten sides and give the boards a curvy and smooth live edge.</p>














<a href="https://alinpanaitiu.comimages/garden-bench-curvy.jpg">
  <figure>
    <figcaption>Curvy live edge of the bench</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 600px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 600px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 600px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/garden-bench-curvy.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/garden-bench-curvy.jpg"
        alt="Curvy live edge of the bench"
        
        
        style="max-width: min(600px, 80vw)"
      >
    </picture>
  </figure>
</a>

<p>For the legs, I stole some more firewood from their pile, where I found some thick branches of unidentified species that were roughly the same length. Stripping the bark with an axe made them look good enough so I screwed them at the four corners of the board. The bench was wobbly with just the legs, so I strengthened it sideways by adding shorter and thinner branches of more unidentified wood between the legs and the center of the board.</p>
<p>I had to do something with the rhomboid ◊ hole, so I filled it with a square 4-by-4 salvaged from a recently dismantled shed, and now the bench has 5 legs. Instead of sawing the leg to size, I left it protruding above the bench and placed another thick salvaged board on top of it to serve as an arm rest, or coffee table, or a place for the bowl of cherries.</p>
<p>For the finish, I burned the bench and the bottom of the legs to get a honey-brown aspect and to make it water resistant. I put a very thin layer of whatever wood lacquer I found in my in-laws shed, just for resistance because I don&rsquo;t like glossy wood.</p>














<a href="https://alinpanaitiu.comimages/garden-bench-2.jpg">
  <figure>
    <figcaption>Side photo of the bench for a better view of the legs</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 400px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 400px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 400px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/garden-bench-2.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/garden-bench-2.jpg"
        alt="Side photo of the bench for a better view of the legs"
        
        
        style="max-width: min(400px, 80vw)"
      >
    </picture>
  </figure>
</a>

<h2 id="other-small-wood-things">Other small wood things</h2>
<h3 id="water-glass-shelf">Water glass shelf</h3>
<p>We don&rsquo;t have much space on the current eating table, so I built a two-shelf stand where we can place the always present water filter jug and the glasses and free up some of the center space.</p>
<p><em>It&rsquo;s incredible how strong just a few screws can be.</em></p>














<a href="https://alinpanaitiu.comimages/water-shelf.jpg">
  <figure>
    <figcaption>Table shelf for holding water filter and glasses</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 400px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/water-shelf.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/water-shelf.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/water-shelf.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/water-shelf.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/water-shelf.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/water-shelf.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/water-shelf.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/water-shelf.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/water-shelf.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/water-shelf.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/water-shelf.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/water-shelf.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/water-shelf.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 400px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/water-shelf.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/water-shelf.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/water-shelf.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/water-shelf.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/water-shelf.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/water-shelf.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/water-shelf.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/water-shelf.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/water-shelf.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/water-shelf.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/water-shelf.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/water-shelf.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/water-shelf.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 400px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/water-shelf.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/water-shelf.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/water-shelf.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/water-shelf.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/water-shelf.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/water-shelf.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/water-shelf.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/water-shelf.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/water-shelf.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/water-shelf.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/water-shelf.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/water-shelf.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/water-shelf.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/water-shelf.jpg"
        alt="Table shelf for holding water filter and glasses"
        
        
        style="max-width: min(400px, 80vw)"
      >
    </picture>
  </figure>
</a>

<h3 id="kaval-stand">Kaval stand</h3>
<p>I thought I should finally do something about the kavals always rolling around on some table or couch throughout the house, so I made a stand from long thin wood boards glued on the side, and finished it with sunflower oil to give it a golden/orange colour.</p>
<p>This way I can always expand it by adding more boards to the side if I want to add more flutes.</p>














<a href="https://alinpanaitiu.comimages/kaval-stand.jpg">
  <figure>
    <figcaption>Stand for holding my kaval collection</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 400px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 400px, 80vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/kaval-stand.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 400px, 80vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/kaval-stand.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/kaval-stand.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/kaval-stand.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/kaval-stand.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/kaval-stand.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/kaval-stand.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/kaval-stand.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/kaval-stand.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/kaval-stand.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/kaval-stand.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/kaval-stand.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/kaval-stand.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/kaval-stand.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/kaval-stand.jpg"
        alt="Stand for holding my kaval collection"
        
        
        style="max-width: min(400px, 80vw)"
      >
    </picture>
  </figure>
</a>

<h3 id="sharpening-block">Sharpening block</h3>
<p>I need to sharpen blades almost daily, be it the pocket knife, axe, plane blade or chisels. So I made a custom sharpening block with the perfect tools for my sharpening technique.</p>


  <figure style="width: min(300px, 80vw); margin: auto">
    <video autoplay loop muted playsinline disablepictureinpicture style="width: 90%; margin: 10px auto; border-radius: 8px;"  >
      <source src="/video/first-sharpening-block-beech.mp4">
  </video>
  <figcaption>Sharpening block, diamond plate with leather strop on a beech base</figcaption>
  </figure>

<p>It has a $5 diamond plate with <code>600</code> grit on one side and a $5 leather strop <em>(a piece of leather belt might work just as well)</em> on the other side. I attached the leather with two small screws at the top so I can take it out easily if I need a flexible strop for my carving gouge for example. It is loaded with diamond paste which can be found for cheap at gemstone cutting online stores <em>(the knife-specific pastes are a lot more expensive and I&rsquo;m not sure why)</em>.</p>
<p>To be honest, a $0.5 green compound <em>(chromium oxide)</em> works just as well for stropping, that&rsquo;s what I used before and still use for my detail carving knives. It gives a smoother edge than the diamond, the disadvantage being that it needs to be re-applied more often on the leather and that you need a bit more blade passes to get the same result. The diamonds seem be cutting faster, but really not much faster.</p>
<h4 id="a-bit-of-a-tangent-on-the-sharpening-topic">A bit of a tangent on the sharpening topic</h4>
<p>I went through all the phases with sharpening tools. I&rsquo;ve used water stones, natural stones, ceramic stones, pull-through carbide sharpeners <em><a href="https://www.youtube.com/watch?v=3jJZdGst8wE"  target="_blank" rel="noopener" >(don&rsquo;t use these)</a></em>, powered belt sharpeners, wheel sharpeners.</p>
<p>Aside from the pull-through sharpeners and the steel rods, all the others work just as well with the right technique. I settled on the diamond plate because they&rsquo;re cheap, stay flat, need zero maintenance, and can cut through any type of metal. Paired with a leather strop, for me it&rsquo;s the simplest way to sharpen.</p>
<p>I recommend this <a href="https://www.youtube.com/watch?v=yLlPiBSP_1U"  target="_blank" rel="noopener" >OUTDOORS55 video</a> for a no-bullshit sharpening tutorial and <a href="https://scienceofsharp.com/home/"  target="_blank" rel="noopener" >the Science of Sharp blog</a> if you&rsquo;re curious what the different sharpening techniques do to an edge under a microscope.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>The complex simplicity of my static websites</title>
      <link>https://alinpanaitiu.com/blog/complex-simplicity-of-static-websites/</link>
      <pubDate>Tue, 08 Aug 2023 10:14:26 +0300</pubDate>
      <author>alin@panaitiu.com (Alin Panaitiu)</author>
      <guid>https://alinpanaitiu.com/blog/complex-simplicity-of-static-websites/</guid>
      <description>
        <![CDATA[<p>It was the spring of 2014, over 9 years ago, just 6 months into my first year of college, when my Computer Architecture teacher stopped in the middle of an <code>assembly</code> exercise to tell us that <a href="https://www.bitdefender.com/company/"  target="_blank" rel="noopener" >Bitdefender</a> is hiring juniors for Malware Researcher positions.</p>
<p>I had no idea what that is, but boy, did it sound cool?&hellip;</p>
<p>I fondly remember how at that time we weren’t chasing high salaries and filtering jobs by programming languages and frameworks. We just wanted to learn something.</p>
<blockquote>
<p>As students, we needed money as well of course, but when I got the job for <code>1750 lei</code> <em>(~€350)</em>, I suddenly became the richest 18 year old in my home town, so it wasn&rsquo;t the top priority.</p>
</blockquote>
<p>And we learnt so much in 2 years.. obscure things like <a href="https://www.baeldung.com/aspectj"  target="_blank" rel="noopener" >AOP</a>, a lot of x86 assembly, reverse engineering techniques which dumped us head first into languages like Java, .NET, <em>ActionScript? (malware authors were creative)</em>.</p>
<p>But most of all, we did tons of <em>Python scripting</em>, and we loved every minute of it. It was my first time getting acquainted with fast tools like <a href="https://www.sublimetext.com"  target="_blank" rel="noopener" >Sublime Text</a> and <a href="https://www.farmanager.com/screenshots.php"  target="_blank" rel="noopener" >FAR Manager</a>. Coming from Notepad++ and Windows Explorer, I felt like a mad hacker with the world at my fingertips.</p>
<p>I’m known as a macOS app dev nowadays, but 9 years ago, I actually started by writing petty Python scripts which spurred the obsessive love I have nowadays for clean <em>accolade-free</em> code and indentation based languages.</p>
<p><em>What does all that have to do with static websites though?</em></p>
<h2 id="pythonic-html">Pythonic HTML</h2>
<p>Well, 5 years ago, when I launched <a href="https://lunar.fyi/"  target="_blank" rel="noopener" >my first macOS app</a>, I found myself needing to create a simple webpage to showcase the app and at the very least, provide a way to download it.</p>
<p>And HTML I did not want to write. The XML like syntax is something I always dreaded, so overfilled with unnecessary <code>&lt;/&gt;</code> symbols that make both writing and reading much more cumbersome. I wanted Python syntax for HTML so I went looking for it.</p>
<p>I went through <a href="https://pugjs.org/language/attributes.html"  target="_blank" rel="noopener" >pug</a>…</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-coffee" data-lang="coffee"><span class="line"><span class="cl"><span class="nx">doctype</span> <span class="nx">html</span>
</span></span><span class="line"><span class="cl"><span class="nx">html</span>
</span></span><span class="line"><span class="cl">  <span class="nx">head</span>
</span></span><span class="line"><span class="cl">    <span class="nx">title</span> <span class="nx">Lunar</span> <span class="o">-</span> <span class="nx">The</span> <span class="nx">defacto</span> <span class="nx">app</span> <span class="k">for</span> <span class="nx">controlling</span> <span class="nx">monitor</span> <span class="nx">brightness</span>
</span></span><span class="line"><span class="cl">    <span class="nx">meta</span><span class="p">(</span><span class="nx">itemprop</span><span class="o">=</span><span class="s">&#39;description&#39;</span> <span class="nx">content</span><span class="o">=</span><span class="s">&#39;...&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">style</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">      <span class="nx">a</span><span class="p">.</span><span class="nx">button</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">background: </span><span class="nx">bisque</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">padding: </span><span class="mf">0.5</span><span class="nx">rem</span> <span class="mi">1</span><span class="nx">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">color: </span><span class="nx">black</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">border</span><span class="o">-</span><span class="nv">radius: </span><span class="mf">0.5</span><span class="nx">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="nx">body</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">display: </span><span class="nx">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">flex</span><span class="o">-</span><span class="nv">direction: </span><span class="nx">column</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">align</span><span class="o">-</span><span class="nv">items: </span><span class="nx">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">justify</span><span class="o">-</span><span class="nv">content: </span><span class="nx">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">text</span><span class="o">-</span><span class="nv">align: </span><span class="nx">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="nx">body</span>
</span></span><span class="line"><span class="cl">    <span class="nx">h1</span><span class="p">(</span><span class="nx">style</span><span class="o">=</span><span class="s">&#39;color: white; font: bold 3rem monospace&#39;</span><span class="p">)</span> <span class="nx">Lunar</span>
</span></span><span class="line"><span class="cl">    <span class="nx">img</span><span class="p">(</span><span class="nx">src</span><span class="o">=</span><span class="s">&#39;https://files.lunar.fyi/display-page.png&#39;</span> <span class="nx">style</span><span class="o">=</span><span class="s">&#39;width: 80%&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">a</span><span class="p">.</span><span class="nx">button</span><span class="p">(</span><span class="nx">href</span><span class="o">=</span><span class="s">&#39;https://files.lunar.fyi/releases/Lunar.dmg&#39;</span><span class="p">)</span> <span class="nx">Download</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><em>pretty, but still needs <code>()</code> for attributes, and I still need accolades in CSS and JS</em></p>
<p>then <a href="https://haml.info/tutorial.html"  target="_blank" rel="noopener" >haml</a>…</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-coffee" data-lang="coffee"><span class="line"><span class="cl"><span class="o">!!!</span>
</span></span><span class="line"><span class="cl"><span class="o">%</span><span class="nx">html</span><span class="p">{</span><span class="nv">lang: </span><span class="s">&#34;en&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">%</span><span class="nx">head</span>
</span></span><span class="line"><span class="cl">    <span class="o">%</span><span class="nx">meta</span><span class="p">{</span><span class="nv">content: </span><span class="s">&#34;text/html; charset=UTF-8&#34;</span><span class="p">,</span> <span class="s">&#34;http-equiv&#34;</span> <span class="nf">=&gt;</span> <span class="s">&#34;Content-Type&#34;</span><span class="p">}</span><span class="o">/</span>
</span></span><span class="line"><span class="cl">    <span class="o">%</span><span class="nx">title</span> <span class="nx">Lunar</span> <span class="o">-</span> <span class="nx">The</span> <span class="nx">defacto</span> <span class="nx">app</span> <span class="k">for</span> <span class="nx">controlling</span> <span class="nx">monitor</span> <span class="nx">brightness</span>
</span></span><span class="line"><span class="cl">    <span class="o">%</span><span class="nx">meta</span><span class="p">{</span><span class="nv">content: </span><span class="s">&#34;...&#34;</span><span class="p">,</span> <span class="nv">itemprop: </span><span class="s">&#34;description&#34;</span><span class="p">}</span><span class="o">/</span>
</span></span><span class="line"><span class="cl">    <span class="o">:</span><span class="nx">css</span>
</span></span><span class="line"><span class="cl">      <span class="nx">a</span><span class="p">.</span><span class="nx">button</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">background: </span><span class="nx">bisque</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">padding: </span><span class="mf">0.5</span><span class="nx">rem</span> <span class="mi">1</span><span class="nx">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">color: </span><span class="nx">black</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">border</span><span class="o">-</span><span class="nv">radius: </span><span class="mf">0.5</span><span class="nx">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="nx">body</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">display: </span><span class="nx">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">flex</span><span class="o">-</span><span class="nv">direction: </span><span class="nx">column</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">align</span><span class="o">-</span><span class="nv">items: </span><span class="nx">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">justify</span><span class="o">-</span><span class="nv">content: </span><span class="nx">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">text</span><span class="o">-</span><span class="nv">align: </span><span class="nx">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">%</span><span class="nx">body</span><span class="p">{</span><span class="nv">style: </span><span class="s">&#34;background: #2e2431; min-height: 90vh&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="o">%</span><span class="nx">h1</span><span class="p">{</span><span class="nv">style: </span><span class="s">&#34;color: white; font: bold 3rem monospace&#34;</span><span class="p">}</span> <span class="nx">Lunar</span>
</span></span><span class="line"><span class="cl">    <span class="o">%</span><span class="nx">img</span><span class="p">{</span><span class="nv">src: </span><span class="s">&#34;https://files.lunar.fyi/display-page.png&#34;</span><span class="p">,</span> <span class="nv">style: </span><span class="s">&#34;width: 80%&#34;</span><span class="p">}</span><span class="o">/</span>
</span></span><span class="line"><span class="cl">    <span class="o">%</span><span class="nx">a</span><span class="p">.</span><span class="nx">button</span><span class="p">{</span><span class="nv">href: </span><span class="s">&#34;https://files.lunar.fyi/releases/Lunar.dmg&#34;</span><span class="p">}</span> <span class="nx">Download</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><em>even more symbols: <code>%</code>, <code>:</code>, <code>=&gt;</code> and <code>/</code> for self-closing tags</em></p>
<p>&hellip;and eventually stumbled upon <a href="https://slim-template.github.io"  target="_blank" rel="noopener" >Slim</a> and its Python counterpart: <a href="https://plim.readthedocs.io/en/latest/"  target="_blank" rel="noopener" >Plim</a></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-stylus" data-lang="stylus"><span class="line"><span class="cl"><span class="err">doctype</span> <span class="nt">html</span>
</span></span><span class="line"><span class="cl"><span class="nt">html</span> <span class="err">lang</span><span class="o">=</span><span class="err">&#34;en&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">head</span>
</span></span><span class="line"><span class="cl">    <span class="nt">title</span> <span class="err">Lunar</span> <span class="o">-</span> <span class="err">The</span> <span class="err">defacto</span> <span class="err">app</span> <span class="k">for</span> <span class="err">controlling</span> <span class="err">monitor</span> <span class="err">brightness</span>
</span></span><span class="line"><span class="cl">    <span class="nt">meta</span> <span class="err">itemprop</span><span class="o">=</span><span class="err">&#34;description&#34;</span> <span class="py">content</span><span class="o">=</span><span class="err">&#34;</span><span class="o">...</span><span class="err">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span><span class="err">stylus</span>
</span></span><span class="line"><span class="cl">      <span class="nt">a</span><span class="p">.</span><span class="nc">button</span>
</span></span><span class="line"><span class="cl">        <span class="py">background</span> <span class="kc">bisque</span>
</span></span><span class="line"><span class="cl">        <span class="py">padding</span> <span class="err">0</span><span class="p">.</span><span class="nc">5rem</span> <span class="err">1rem</span>
</span></span><span class="line"><span class="cl">        <span class="py">color</span> <span class="kc">black</span>
</span></span><span class="line"><span class="cl">        <span class="py">border</span><span class="o">-</span><span class="err">radius</span> <span class="err">0</span><span class="p">.</span><span class="nc">5rem</span>
</span></span><span class="line"><span class="cl">      <span class="nt">body</span>
</span></span><span class="line"><span class="cl">        <span class="py">display</span> <span class="kc">flex</span>
</span></span><span class="line"><span class="cl">        <span class="kc">flex</span><span class="o">-</span><span class="py">direction</span> <span class="kc">column</span>
</span></span><span class="line"><span class="cl">        <span class="py">align-items</span> <span class="kc">center</span>
</span></span><span class="line"><span class="cl">        <span class="kc">justify</span><span class="o">-</span><span class="py">content</span> <span class="kc">center</span>
</span></span><span class="line"><span class="cl">        <span class="kc">text</span><span class="o">-</span><span class="err">align</span> <span class="nt">center</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nt">body</span> <span class="nt">style</span><span class="o">=</span><span class="err">&#34;</span><span class="py">background</span><span class="p">:</span> <span class="mh">#2e2431</span><span class="p">;</span> <span class="py">min-height</span><span class="p">:</span> <span class="err">90vh&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">h1</span> <span class="nt">style</span><span class="o">=</span><span class="err">&#34;</span><span class="py">color</span><span class="p">:</span> <span class="kc">white</span><span class="p">;</span> <span class="py">font</span><span class="p">:</span> <span class="kc">bold</span> <span class="err">3rem</span> <span class="err">monospace&#34;</span> <span class="err">Lunar</span>
</span></span><span class="line"><span class="cl">    <span class="nt">img</span> <span class="err">src</span><span class="o">=</span><span class="err">&#34;https</span><span class="p">:</span><span class="o">//</span><span class="err">files</span><span class="p">.</span><span class="nc">lunar</span><span class="p">.</span><span class="nc">fyi</span><span class="o">/</span><span class="py">display</span><span class="o">-</span><span class="py">page</span><span class="p">.</span><span class="nc">png</span><span class="err">&#34;</span> <span class="nt">style</span><span class="o">=</span><span class="err">&#34;</span><span class="py">width</span><span class="p">:</span> <span class="err">80</span><span class="o">%</span><span class="err">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">a</span><span class="p">.</span><span class="nc">button</span> <span class="err">href</span><span class="o">=</span><span class="err">&#34;https</span><span class="p">:</span><span class="o">//</span><span class="err">files</span><span class="p">.</span><span class="nc">lunar</span><span class="p">.</span><span class="nc">fyi</span><span class="o">/</span><span class="err">releases</span><span class="o">/</span><span class="err">Lunar</span><span class="p">.</span><span class="nc">dmg</span><span class="err">&#34;</span> <span class="err">Download</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><em>ahhh.. so clean!</em></p>
<p>Here&rsquo;s how that example looks like if I would have to write it as HTML:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>Lunar - The defacto app for controlling monitor brightness<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">itemprop</span><span class="o">=</span><span class="s">&#34;description&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;...&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">a</span><span class="p">.</span><span class="nc">button</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">background</span><span class="p">:</span> <span class="kc">bisque</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="k">padding</span><span class="p">:</span> <span class="mf">0.5</span><span class="kt">rem</span> <span class="mi">1</span><span class="kt">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="k">color</span><span class="p">:</span> <span class="kc">black</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="k">border-radius</span><span class="p">:</span> <span class="mf">0.5</span><span class="kt">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="nt">body</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="k">flex-direction</span><span class="p">:</span> <span class="kc">column</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="k">align-items</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="k">justify-content</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">h1</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;color: white; font: bold 3rem monospace&#34;</span><span class="p">&gt;</span>Lunar<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://files.lunar.fyi/display-page.png&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;width: 80%&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;button&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://files.lunar.fyi/releases/Lunar.dmg&#34;</span><span class="p">&gt;</span>Download<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><em>not particulary hard to read, but writing would need a lot of Shift-holding and repeating tags</em></p>
<p>The thing I like most about Plim, and why I stuck with it, is that it can parse my other favorite <em>symbol-hating</em> languages without additional configuration:</p>
<ul>
<li>Python for abstracting away repeating structures</li>
<li>Stylus for writing <code>style</code> tags</li>
<li>CoffeeScript for the <code>script</code> tags</li>
<li>Markdown for long text content</li>
</ul>
<p>Here&rsquo;s a more complex example to showcase the above features <em><a href="https://youtu.be/cVAcRH9b44w?t=55"  target="_blank" rel="noopener" >(might require sunglasses)</a></em>:</p>
<p><em>example of writing a HDR page section, similar to the one on <a href="https://lunar.fyi#xdr"  target="_blank" rel="noopener" >lunar.fyi</a></em></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-coffee" data-lang="coffee"><span class="line"><span class="cl"><span class="o">---!</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># use Python to generate the dynamic image sizes for the srcset attr
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="nv">WIDTHS = </span><span class="p">[</span><span class="mi">1920</span><span class="p">,</span> <span class="mi">1280</span><span class="p">,</span> <span class="mi">1024</span><span class="p">,</span> <span class="mi">768</span><span class="p">,</span> <span class="mi">640</span><span class="p">,</span> <span class="mi">320</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">def</span> <span class="nx">srcset</span><span class="p">(</span><span class="nx">image</span><span class="p">,</span> <span class="nx">ext</span><span class="p">,</span> <span class="nx">page_fraction</span><span class="o">=</span><span class="mf">1.0</span><span class="p">)</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s">&#39;,&#39;</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="nx">f</span><span class="s">&#39;/img/{image}/{width}.{ext} {width // page_fraction:.0f}w&#39;</span>
</span></span><span class="line"><span class="cl">      <span class="k">for</span> <span class="nx">width</span> <span class="k">in</span> <span class="nx">WIDTHS</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">doctype</span> <span class="nx">html</span>
</span></span><span class="line"><span class="cl"><span class="nx">html</span> <span class="nx">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">head</span>
</span></span><span class="line"><span class="cl">    <span class="o">-</span><span class="nx">stylus</span>
</span></span><span class="line"><span class="cl">      <span class="c1"># use Stylus to do a readable media query that checks for wide color gamut
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">      <span class="nx">@media</span> <span class="nx">screen</span> <span class="o">and</span> <span class="p">(</span><span class="nx">color</span><span class="o">-</span><span class="nv">gamut: </span><span class="nx">p3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nx">@supports</span> <span class="p">(</span><span class="o">-</span><span class="nx">webkit</span><span class="o">-</span><span class="nx">backdrop</span><span class="o">-</span><span class="nv">filter: </span><span class="nx">brightness</span><span class="p">(</span><span class="mf">1.5</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">          <span class="nx">section</span><span class="c1">#xdr
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="o">-</span><span class="nx">webkit</span><span class="o">-</span><span class="nx">backdrop</span><span class="o">-</span><span class="nv">filter: </span><span class="nx">brightness</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="nv">filter: </span><span class="nx">brightness</span><span class="p">(</span><span class="mf">1.5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">body</span>
</span></span><span class="line"><span class="cl">    <span class="nx">section</span><span class="c1">#xdr
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="nx">picture</span>
</span></span><span class="line"><span class="cl">        <span class="nx">source</span> <span class="nx">type</span><span class="o">=</span><span class="s">&#34;image/webp&#34;</span> <span class="nx">srcset</span><span class="o">=</span><span class="nx">$</span><span class="p">{</span><span class="nx">srcset</span><span class="p">(</span><span class="s">&#39;xdr&#39;</span><span class="p">,</span> <span class="s">&#39;webp&#39;</span><span class="p">,</span> <span class="mf">0.3</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl">        <span class="nx">source</span> <span class="nx">type</span><span class="o">=</span><span class="s">&#34;image/png&#34;</span> <span class="nx">srcset</span><span class="o">=</span><span class="nx">$</span><span class="p">{</span><span class="nx">srcset</span><span class="p">(</span><span class="s">&#39;xdr&#39;</span><span class="p">,</span> <span class="s">&#39;png&#39;</span><span class="p">,</span> <span class="mf">0.3</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="o">-</span><span class="nx">md</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># write markdown that renders as inline HTML
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">        <span class="nx">Unlock</span> <span class="nx">the</span> <span class="nx">full</span> <span class="nx">brightness</span> <span class="k">of</span> <span class="nx">your</span> <span class="nx">XDR</span> <span class="nx">display</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nx">The</span> <span class="o">**</span><span class="mi">2021</span> <span class="nx">MacBook</span> <span class="nx">Pro</span><span class="o">**</span> <span class="o">and</span> <span class="nx">the</span> <span class="o">**</span><span class="nx">Pro</span> <span class="nx">Display</span> <span class="nx">XDR</span><span class="o">**</span> <span class="nx">feature</span> <span class="nx">an</span> <span class="nx">incredibly</span> <span class="nx">bright</span> <span class="nx">panel</span> <span class="o">*</span><span class="p">(</span><span class="mi">1600</span> <span class="nx">nits</span><span class="o">!</span><span class="p">)</span><span class="o">*</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">but</span> <span class="nx">which</span> <span class="o">is</span> <span class="nx">locked</span> <span class="k">by</span> <span class="nx">macOS</span> <span class="nx">to</span> <span class="nx">a</span> <span class="nx">third</span> <span class="k">of</span> <span class="nx">its</span> <span class="nx">potential</span> <span class="o">*</span><span class="p">(</span><span class="mi">500</span> <span class="nx">nits</span><span class="p">...)</span><span class="o">*</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nx">Lunar</span> <span class="nx">can</span> <span class="o">**</span><span class="nx">remove</span> <span class="nx">the</span> <span class="nx">brightness</span> <span class="nx">lock</span><span class="o">**</span> <span class="o">and</span> <span class="nx">allow</span> <span class="nx">you</span> <span class="nx">to</span> <span class="nx">increase</span> <span class="nx">the</span> <span class="nx">brightness</span> <span class="nx">past</span> <span class="nx">that</span> <span class="nx">limit</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">-</span><span class="nx">coffee</span>
</span></span><span class="line"><span class="cl">      <span class="c1"># use CoffeeScript to detect if the browser might not support HDR
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">      <span class="nv">$ = </span><span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span>
</span></span><span class="line"><span class="cl">      <span class="nv">safari = </span><span class="sr">/^((?!chrome|android).)*safari/i</span><span class="p">.</span><span class="nx">test</span> <span class="nx">navigator</span><span class="p">.</span><span class="nx">userAgent</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="nb">window</span><span class="p">.</span><span class="nv">onload = </span><span class="nf">() -&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="o">not</span> <span class="nx">safari</span>
</span></span><span class="line"><span class="cl">          <span class="nx">$</span><span class="p">(</span><span class="s">&#39;#xdr&#39;</span><span class="p">)</span><span class="o">?</span><span class="p">.</span><span class="nv">style.filter = </span><span class="s">&#34;none&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div>

<style type="text/css">
  a[href="https://youtu.be/cVAcRH9b44w?t=55"] {
    display: none;
  }
  @media screen and (color-gamut: p3) {
    video#xdr {
      display: block !important;
    }
    a[href="https://youtu.be/cVAcRH9b44w?t=55"] {
      display: inline !important;
    }
  }
</style>

<video id="xdr" loop muted playsinline disablepictureinpicture style="width: 100%; margin: 10px auto; border-radius: 8px; display: none;" >
  <source src="/video/xdr-blog-h265.mp4" type="video/mp4; codecs=hvc1">
  <source src="/video/xdr-blog.mp4" type="video/mp4; codecs=avc1">
</video>

<p>And best of all, there is no crazy toolchain, bundler or dependency hell involved. No project structure needed, no configuration files. I can just write a <code>contact.plim</code> file, compile it with <code>plimc</code> to a readable <code>contact.html</code> and have a <code>/contact</code> page ready!</p>
<p>So that’s how it went with my app: I wrote a simple <code>index.plim</code>, dropped it on Netlify and went on with my day.</p>
<h4 id="complexity-cost">Complexity Cost</h4>
<ul>
<li>1 pip install for getting the Plim CLI</li>
<li>1 npm install for getting <code>stylus</code> and <code>coffeescript</code> <em>(optional)</em></li>
<li>1 build command for generating the HTML files</li>
</ul>
<h2 id="complex-simplicity">Complex simplicity</h2>
<p>The app managed to get quite a bit of attention, and while I kept developing it, for the next 4 years the website remained the same <em>heading - image - download button</em> single page. It was only a side project after all.</p>
<p>Working for US companies from Romania made good money, but it was so tiring to get through 3h of video meetings daily, standups, syntax nitpicking in PR review, SCRUM bullshit, JIRA, task writing, task assigning, estimating task time in <em>T-shirt sizes??</em></p>
<p>In <strong>April 2021</strong> I finally got tired of writing useless code and selling my time like it was some grain silo I could always fill back up with even more work…</p>
<p>I bet on <a href="https://alinpanaitiu.com/blog/journey-to-ddc-on-m1-macs/"  target="_blank" rel="noopener" >developing my app further</a>. Since my college days, I always chose the work that helps me learn new concepts. At some point I had to understand that I learnt enough and had to start sharing. This time I really wanted to write software that helped people, and was willing to spend my savings on it.</p>
<h3 id="comically-stuffed-stylesheets">Comically Stuffed Stylesheets</h3>
<p>A more complete app also required a more complete presentation website, but the styling was getting out of hand. You would think that with <code>flexbox</code> and <code>grids</code>, you can just write vanilla CSS these days, but just adding a bit of variation requires constant jumping between the CSS and HTML files.</p>
<blockquote>
<p>A presentation page is usually only 10% HTML markup. The rest is a ton of styling and copy text, so I wanted to optimize my dev experience for that.</p>
</blockquote>
<p>There’s no “go to definition” on HTML <code>.classes</code> or <code>#ids</code> because their styles can be defined <strong>✨anywhere✨</strong>. So you have to Cmd-F like a madman and be very rigorous on your CSS structure.</p>
<p>The controversial but very clever solution to this was <a href="https://tailwindcss.com"  target="_blank" rel="noopener" >Tailwind CSS</a>: a large collection of short predefined classes that <em>mostly</em> style just the property they hint at.</p>
<p>For example in the first code block I had to write a non-reusable 5-line style to center the body contents.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="nt">body</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">flex-direction</span><span class="p">:</span> <span class="kc">column</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">align-items</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">justify-content</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>With Tailwind, I would have written the <code>body</code> tag like so:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sass" data-lang="sass"><span class="line"><span class="cl"><span class="nt">body</span><span class="nc">.flex.flex-col.justify-center.items-center.text-center</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>That might not seem like much, some would argue that it’s even a lot less readable than the CSS one. Can’t I <em>just</em> define a <code>.center</code> class that I can reuse?</p>
<p>Well, think about a few things:</p>


<div id="tailwind-usefulness"></div>

<ul>
<li>this might repeat on many sections of the page, but with slight variations <em>(what if I want a centered row, or longer paragraphs of text aligned to the left)</em></li>
<li><strong>responsive</strong> sections might need to alter layout (e.g. vertical on mobile, horizontal on desktop) and media queries will quickly blow up the style size
<ul>
<li><code>.md:flex-row.flex-col</code> is what you would write in Tailwind</li>
</ul>
</li>
<li>adding <strong>dark/light mode</strong> support is yet another media query
<ul>
<li><code>.dark:bg-white.bg-black</code> looks simple enough</li>
</ul>
</li>
<li>interactions like <strong>hover effects</strong>, complex <em>shadows</em> and filters like <em>blur</em> and <em>brightness</em> is syntax that’s often forgotten
<ul>
<li><code>.shadow.hover:shadow-xl</code> creates a lift off the page effect on hover by making the shadow larger</li>
<li><code>.blur.active:blur-none</code> un-blurs an element when you click on it</li>
</ul>
</li>
<li>choosing <strong><a href="https://tailwindcss.com/docs/customizing-colors"  target="_blank" rel="noopener" >colors</a></strong> and reusing them needs a lot of attention
<ul>
<li><code>.bg-red-500.text-white</code> sets white text on saturated red</li>
<li><code>red-100</code> is less saturated, towards white</li>
<li><code>red-900</code> is darker, towards black</li>
</ul>
</li>
</ul>


<style type="text/css">
  #tailwind-usefulness + ul > li:nth-child(4) > ul > li > code {
    transition: box-shadow 0.55s ease-in-out, filter 0.55s ease-in-out;
  }
  #tailwind-usefulness + ul > li:nth-child(4) > ul > li > code:hover {
    transition: box-shadow 0.25s ease-in-out, filter 0.15s ease-out;
  }

  #tailwind-usefulness + ul > li:nth-child(3) > ul > li:nth-of-type(1) > code {
    background: black !important;
    color: rgb(255, 200, 149) !important;
  }

  @media (prefers-color-scheme: dark) {
    #tailwind-usefulness + ul > li:nth-child(3) > ul > li:nth-of-type(1) > code {
      background: white !important;
      color: hsl(19, 72%, 32%) !important;
    }
  }

  #tailwind-usefulness + ul > li:nth-child(4) > ul > li:nth-of-type(1) > code {
    box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
  }
  #tailwind-usefulness + ul > li:nth-child(4) > ul > li:nth-of-type(1) > code:hover {
    box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
  }
  @media (prefers-color-scheme: dark) {
    #tailwind-usefulness + ul > li:nth-child(4) > ul > li:nth-of-type(1) > code:hover {
      box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.5), 0 8px 10px -6px rgb(0 0 0 / 0.7);
    }
  }
  #tailwind-usefulness + ul > li:nth-child(4) > ul > li:nth-of-type(2) > code {
    filter: blur(4px);
  }
  #tailwind-usefulness + ul > li:nth-child(4) > ul > li:nth-of-type(2) > code:active {
    filter: blur(0) !important;
  }
  #tailwind-usefulness + ul > li:nth-child(5) > ul > li:nth-of-type(1) > code {
    background: #ef4444 !important;
    color: white !important;
  }
  #tailwind-usefulness + ul > li:nth-child(5) > ul > li:nth-of-type(2) > code {
    background: #fee2e2 !important;
    color: black !important;
  }
  #tailwind-usefulness + ul > li:nth-child(5) > ul > li:nth-of-type(3) > code {
    background: #7f1d1d !important;
    color: white !important;
  }
</style>

<p>Sure, long lines of classes might not be so readable, but neither are long files of CSS styling. At least the Tailwind classes are right there at your fingertips, and you can replace a <code>-lg</code> with a <code>-xl</code>  to quickly fine tune your style.</p>
<h4 id="complexity-cost-1">Complexity Cost:</h4>
<ul>
<li>1 command added for building the minimal CSS from the classes used</li>
<li>1 npm install for getting the Tailwind CLI</li>
<li>1 config file for defining custom colors, animations etc. <em>(optional)</em></li>
</ul>
<h3 id="responsive-images">Responsive images</h3>
<p>So many people obsess over the size of their JS or CSS, but fail to realize that the bulk of their page is <em>unnecessarily large and not well compressed images</em>.</p>
<p>Of course, I was one of those people.</p>
<p>For years, my app’s website had a screenshot of its window as an uncompressed PNG, loading slowly from top to bottom and chugging the user’s bandwidth.</p>














<a href="https://alinpanaitiu.comimages/old-lunar-website.jpg">
  <figure>
    <figcaption>Old Lunar website</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 610px, 70vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 610px, 70vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 610px, 70vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/old-lunar-website.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/old-lunar-website.jpg"
        alt="Old Lunar website"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>I had no idea, but screenshots and screen recordings are most of the time up to 10x larger than their visually indistinguishable compressed counterparts.</p>
<blockquote>
<p>I even wrote an app to fix that since I’m constantly sending screenshots to people and was tired of waiting for 5MB images to upload in rapid chats.</p>
<p>It’s called <strong><a href="https://lowtechguys.com/clop"  target="_blank" rel="noopener" >Clop</a></strong> if you want to check it out.</p>
<p>Yes, just like <a href="https://lite.cnn.com/2023/06/16/tech/clop-ransomware-attack-explainer/index.html"  target="_blank" rel="noopener" >that famous ransomware</a>, it wasn’t that famous at the time of naming the app.</p>
</blockquote>
<p>I needed a lot more images to showcase the features of an app controlling monitor brightness and colors, so I had to improve on this.</p>
<p>Delivering the smallest image necessary to the user is quite a complex endeavour:</p>
<ol>
<li>Optimize the image using <a href="https://imageoptim.com/mac"  target="_blank" rel="noopener" >ImageOptim</a></li>
<li>Resize it to fit multiple screen sizes using <a href="https://www.libvips.org/API/current/Using-vipsthumbnail.html"  target="_blank" rel="noopener" >vipsthumbnail</a></li>
<li>Figure out what fraction of the page width will be occupied by the image</li>
<li>Write a suitable <a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images#resolution_switching_different_sizes"  target="_blank" rel="noopener" >srcset</a> attribute to load the suitable image</li>
<li><em>Optional:</em> convert the image to formats like <code>webp</code>, <code>avif</code> or JPEG XL for smallest file size</li>
</ol>
<p>I did so much of that work manually in the past… thankfully nowadays I have <a href="https://imgproxy.net"  target="_blank" rel="noopener" >imgproxy</a> to do the encoding, optimization and resizing for me.</p>
<p>I just have to write the srcset, for which I defined Plim and Python functions to do the string wrangling for me.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sass" data-lang="sass"><span class="line"><span class="cl"><span class="na">-def</span><span class="err"> </span><span class="na">image(img,</span><span class="err"> </span><span class="na">ext</span><span class="o">=</span><span class="s1">&#39;png&#39;</span><span class="o">,</span> <span class="n">factor</span><span class="o">=</span><span class="mi">0</span><span class="mf">.4</span><span class="o">,</span> <span class="n">mobile_factor</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nt">picture</span>
</span></span><span class="line"><span class="cl"><span class="err">        </span><span class="na">-call</span><span class="err"> </span><span class="na">img</span><span class="o">=</span><span class="err">${</span><span class="n">img</span><span class="err">}</span> <span class="n">ext</span><span class="o">=</span><span class="err">${</span><span class="n">ext</span><span class="err">}</span> <span class="n">factor</span><span class="o">=</span><span class="err">${</span><span class="n">factor</span><span class="err">}</span> <span class="n">mobile_factor</span><span class="o">=</span><span class="err">${</span><span class="n">mobile_factor</span><span class="err">}</span> <span class="n">self</span><span class="o">:</span><span class="n">sources</span>
</span></span><span class="line"><span class="cl"><span class="err">        </span><span class="na">img</span><span class="err"> </span><span class="na">srcset</span><span class="o">=</span><span class="err">${</span><span class="nf">srcset</span><span class="p">(</span><span class="n">img</span><span class="o">,</span> <span class="n">ext</span><span class="o">,</span> <span class="n">factor</span><span class="p">)</span><span class="err">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="na">-def</span><span class="err"> </span><span class="na">sources(img,</span><span class="err"> </span><span class="na">ext</span><span class="o">=</span><span class="s1">&#39;png&#39;</span><span class="o">,</span> <span class="n">factor</span><span class="o">=</span><span class="mi">0</span><span class="mf">.4</span><span class="o">,</span> <span class="n">mobile_factor</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">source</span><span class="err"> </span><span class="na">type</span><span class="o">=</span><span class="s2">&#34;image/avif&#34;</span> <span class="n">srcset</span><span class="o">=</span><span class="err">${</span><span class="nf">srcset</span><span class="p">(</span><span class="n">img</span><span class="o">,</span> <span class="n">ext</span><span class="o">,</span> <span class="n">mobile_factor</span><span class="o">,</span> <span class="n">convert_to</span><span class="o">=</span><span class="s2">&#34;avif&#34;</span><span class="p">)</span><span class="err">}</span> <span class="n">media</span><span class="o">=</span><span class="s2">&#34;(max-width: 767px)&#34;</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">source</span><span class="err"> </span><span class="na">type</span><span class="o">=</span><span class="s2">&#34;image/avif&#34;</span> <span class="n">srcset</span><span class="o">=</span><span class="err">${</span><span class="nf">srcset</span><span class="p">(</span><span class="n">img</span><span class="o">,</span> <span class="n">ext</span><span class="o">,</span> <span class="n">factor</span><span class="o">,</span> <span class="n">convert_to</span><span class="o">=</span><span class="s2">&#34;avif&#34;</span><span class="p">)</span><span class="err">}</span> <span class="n">media</span><span class="o">=</span><span class="s2">&#34;(min-width: 768px)&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">source</span><span class="err"> </span><span class="na">type</span><span class="o">=</span><span class="s2">&#34;image/webp&#34;</span> <span class="n">srcset</span><span class="o">=</span><span class="err">${</span><span class="nf">srcset</span><span class="p">(</span><span class="n">img</span><span class="o">,</span> <span class="n">ext</span><span class="o">,</span> <span class="n">mobile_factor</span><span class="o">,</span> <span class="n">convert_to</span><span class="o">=</span><span class="s2">&#34;webp&#34;</span><span class="p">)</span><span class="err">}</span> <span class="n">media</span><span class="o">=</span><span class="s2">&#34;(max-width: 767px)&#34;</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">source</span><span class="err"> </span><span class="na">type</span><span class="o">=</span><span class="s2">&#34;image/webp&#34;</span> <span class="n">srcset</span><span class="o">=</span><span class="err">${</span><span class="nf">srcset</span><span class="p">(</span><span class="n">img</span><span class="o">,</span> <span class="n">ext</span><span class="o">,</span> <span class="n">factor</span><span class="o">,</span> <span class="n">convert_to</span><span class="o">=</span><span class="s2">&#34;webp&#34;</span><span class="p">)</span><span class="err">}</span> <span class="n">media</span><span class="o">=</span><span class="s2">&#34;(min-width: 768px)&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">source</span><span class="err"> </span><span class="na">type</span><span class="o">=</span><span class="s2">&#34;image/${ext}&#34;</span> <span class="n">srcset</span><span class="o">=</span><span class="err">${</span><span class="nf">srcset</span><span class="p">(</span><span class="n">img</span><span class="o">,</span> <span class="n">ext</span><span class="o">,</span> <span class="n">mobile_factor</span><span class="p">)</span><span class="err">}</span> <span class="n">media</span><span class="o">=</span><span class="s2">&#34;(max-width: 767px)&#34;</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">source</span><span class="err"> </span><span class="na">type</span><span class="o">=</span><span class="s2">&#34;image/${ext}&#34;</span> <span class="n">srcset</span><span class="o">=</span><span class="err">${</span><span class="nf">srcset</span><span class="p">(</span><span class="n">img</span><span class="o">,</span> <span class="n">ext</span><span class="o">,</span> <span class="n">factor</span><span class="p">)</span><span class="err">}</span> <span class="n">media</span><span class="o">=</span><span class="s2">&#34;(min-width: 768px)&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">WIDTHS</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1920</span><span class="p">,</span> <span class="mi">1280</span><span class="p">,</span> <span class="mi">1024</span><span class="p">,</span> <span class="mi">768</span><span class="p">,</span> <span class="mi">640</span><span class="p">,</span> <span class="mi">320</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">imgurl</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">ext</span><span class="o">=</span><span class="s2">&#34;png&#34;</span><span class="p">,</span> <span class="n">convert_to</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">conversion</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;@</span><span class="si">{</span><span class="n">convert_to</span><span class="si">}</span><span class="s2">&#34;</span> <span class="k">if</span> <span class="n">convert_to</span> <span class="k">else</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="sa">f</span><span class="s2">&#34;https://img.panaitiu.com/_/</span><span class="si">{</span><span class="n">width</span><span class="si">}</span><span class="s2">/plain/https://lunar.fyi/img/</span><span class="si">{</span><span class="n">urllib</span><span class="o">.</span><span class="n">parse</span><span class="o">.</span><span class="n">quote</span><span class="p">(</span><span class="n">image</span><span class="p">)</span><span class="si">}</span><span class="s2">.</span><span class="si">{</span><span class="n">ext</span><span class="si">}{</span><span class="n">conversion</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">srcset</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">ext</span><span class="o">=</span><span class="s2">&#34;png&#34;</span><span class="p">,</span> <span class="n">factor</span><span class="o">=</span><span class="mf">1.0</span><span class="p">,</span> <span class="n">convert_to</span><span class="o">=</span><span class="s2">&#34;&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;,&#34;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">imgurl</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">ext</span><span class="p">,</span> <span class="n">convert_to</span><span class="p">)</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">width</span> <span class="o">//</span> <span class="n">factor</span><span class="si">:</span><span class="s2">.0f</span><span class="si">}</span><span class="s2">w&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">width</span> <span class="ow">in</span> <span class="n">WIDTHS</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="complexity-cost-2">Complexity Cost</h4>
<ul>
<li>1 imgproxy server that needs to run somewhere publicly available, be kept alive and secure</li>
<li>some Python and Plim code for generating srcsets</li>
</ul>
<h3 id="hot-reloading">Hot reloading</h3>
<p>After 2 weeks of editing the page, Cmd-Tab to the browser, Cmd-R to refresh, I got really tired of this routine.</p>
<p>I worked with Next.js before on <a href="https://noiseblend.com"  target="_blank" rel="noopener" >Noiseblend</a> and loved how each file change automatically gets refreshed in the browser. Instantly and in-place as well, not a full page refresh. I got the same experience when I worked with React Native.</p>
<p>There should be something for static pages too, I thought. Well it turns out there is, it’s called <a href="https://nitoyon.github.io/livereloadx/"  target="_blank" rel="noopener" >LiveReload</a> and I had to slap my forehead for not searching for it sooner.</p>
<p>After installing the browser extension, and running the <code>livereloadx --static</code> file watcher, I got my hot reloading dev experience back.</p>
<blockquote>
<p>Actually now that I think about it, Hugo has super fast hot reloading, how does it accomplish that? Yep, turns out <a href="https://gohugo.io/getting-started/usage/#livereload"  target="_blank" rel="noopener" >Hugo uses LiveReload</a> as well.</p>
</blockquote>
<h4 id="complexity-cost-3">Complexity Cost</h4>
<ul>
<li>1 more command to run in 1 more terminal panel, <a href="https://pypi.org/project/multiplex/"  target="_blank" rel="noopener" >multiplex</a> helps with that</li>
<li>1 browser extension to install and hope it’s not compromised or sold to a data thief</li>
<li>1 npm install for getting the livereloadx CLI</li>
</ul>
<h3 id="contact-pages">Contact pages</h3>
<p>After releasing the new app version, many things were broken, expectedly.</p>
<p>People tried to reach me in so many ways: Github issues, personal email, through the app licensing provider, even Facebook Messenger. I had no idea that including an official way of contact would be so vital.</p>
<p>And I had no idea how to even do it. A contact form needs, like, a server to <code>POST</code> to, right? And that server needs to notify me in some way, and then I have to respond to the user in some other way… <em>sigh</em></p>
<p>I thought about those chat bubbles that a lot of sites have, but I used them on Noiseblend and did not like the experience. Plus I dislike seeing them myself, they’re an eyesore and a nuisance obscuring page content and possibly violating privacy.</p>
<p>After long searches, <em>not sure why it took so long</em>, I stumbled upon <a href="https://formspark.io"  target="_blank" rel="noopener" >Formspark</a>: a service that gives you a link to <code>POST</code> your form to, and they send you an email with the form contents. The email will contain the user email in <code>ReplyTo</code> so I can just reply normally from my mail client.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sass" data-lang="sass"><span class="line"><span class="cl"><span class="na">form</span><span class="err"> </span><span class="na">action</span><span class="o">=</span><span class="s2">&#34;https://submit-form.com/some-random-id&#34;</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">label</span><span class="err"> </span><span class="na">for</span><span class="o">=</span><span class="s2">&#34;name&#34;</span> <span class="n">Name</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">input#from</span><span class="err"> </span><span class="na">hidden</span><span class="o">=</span><span class="s2">&#34;true&#34;</span> <span class="n">name</span><span class="o">=</span><span class="s2">&#34;_email.from&#34;</span> <span class="n">type</span><span class="o">=</span><span class="s2">&#34;text&#34;</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">input#name</span><span class="err"> </span><span class="na">name</span><span class="o">=</span><span class="s2">&#34;name&#34;</span> <span class="n">placeholder</span><span class="o">=</span><span class="s2">&#34;John Doe&#34;</span> <span class="n">required</span><span class="o">=</span><span class="s2">&#34;&#34;</span> <span class="n">type</span><span class="o">=</span><span class="s2">&#34;text&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">label</span><span class="err"> </span><span class="na">for</span><span class="o">=</span><span class="s2">&#34;email&#34;</span> <span class="n">Email</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">input#email</span><span class="err"> </span><span class="na">name</span><span class="o">=</span><span class="s2">&#34;email&#34;</span> <span class="n">placeholder</span><span class="o">=</span><span class="s2">&#34;john@doe.com&#34;</span> <span class="n">required</span><span class="o">=</span><span class="s2">&#34;&#34;</span> <span class="n">type</span><span class="o">=</span><span class="s2">&#34;email&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">label</span><span class="err"> </span><span class="na">for</span><span class="o">=</span><span class="s2">&#34;subject&#34;</span> <span class="n">Subject</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">input#email-subject</span><span class="err"> </span><span class="na">hidden</span><span class="o">=</span><span class="s2">&#34;true&#34;</span> <span class="n">name</span><span class="o">=</span><span class="s2">&#34;_email.subject&#34;</span> <span class="n">type</span><span class="o">=</span><span class="s2">&#34;text&#34;</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">input#subject</span><span class="err"> </span><span class="na">name</span><span class="o">=</span><span class="s2">&#34;subject&#34;</span> <span class="n">placeholder</span><span class="o">=</span><span class="s2">&#34;What&#39;s this message about?&#34;</span> <span class="n">required</span><span class="o">=</span><span class="s2">&#34;&#34;</span> <span class="n">type</span><span class="o">=</span><span class="s2">&#34;text&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">label</span><span class="err"> </span><span class="na">for</span><span class="o">=</span><span class="s2">&#34;message&#34;</span> <span class="n">Message</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">textarea#message</span><span class="err"> </span><span class="na">name</span><span class="o">=</span><span class="s2">&#34;message&#34;</span> <span class="n">placeholder</span><span class="o">=</span><span class="s2">&#34;Something about our apps perhaps&#34;</span> <span class="n">required</span><span class="o">=</span><span class="s2">&#34;&#34;</span> <span class="n">type</span><span class="o">=</span><span class="s2">&#34;text&#34;</span> <span class="n">rows</span><span class="o">=</span><span class="s2">&#34;6&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">-coffee</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">#</span><span class="err"> </span><span class="na">Custom</span><span class="err"> </span><span class="na">subject</span><span class="err"> </span><span class="na">and</span><span class="err"> </span><span class="na">name</span><span class="o">:</span> <span class="n">https</span><span class="o">://</span><span class="n">documentation</span><span class="o">.</span><span class="n">formspark</span><span class="o">.</span><span class="n">io</span><span class="o">/</span><span class="n">customization</span><span class="o">/</span><span class="ow">not</span><span class="n">ification-email</span><span class="o">.</span><span class="n">html</span><span class="mh">#subjec</span><span class="n">t</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">nameInput</span><span class="o"> =</span> <span class="n">document</span><span class="o">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="s2">&#34;name&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">fromInput</span><span class="o"> =</span> <span class="n">document</span><span class="o">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="s2">&#34;from&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">nameInput.addEventListener</span><span class="err"> </span><span class="na">&#39;input&#39;,</span><span class="err"> </span><span class="na">(event)</span><span class="err"> </span><span class="na">-&gt;</span><span class="err"> </span><span class="na">fromInput.value</span><span class="o"> =</span> <span class="n">event</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">value</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">subjectInput</span><span class="o"> =</span> <span class="n">document</span><span class="o">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="s2">&#34;subject&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">emailSubjectInput</span><span class="o"> =</span> <span class="n">document</span><span class="o">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="s2">&#34;email-subject&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="err">    </span><span class="na">subjectInput.addEventListener</span><span class="err"> </span><span class="na">&#39;input&#39;,</span><span class="err"> </span><span class="na">(event)</span><span class="err"> </span><span class="na">-&gt;</span><span class="err"> </span><span class="na">emailSubjectInput.value</span><span class="o"> =</span> <span class="n">event</span><span class="o">.</span><span class="n">target</span><span class="o">.</span><span class="n">value</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="complexity-cost-4">Complexity Cost</h4>
<p>None, I guess. I just hope that the prolific but <a href="https://github.com/botre"  target="_blank" rel="noopener" >unique Formspark dev</a> doesn’t die or get kidnapped or something.</p>
<h2 id="and-you-call-this-simple">And you call this “simple”?</h2>
<p>It’s not. Really. It’s crazy what I had to go through to get to a productive setup that fits my needs.</p>
<p>One could say I could have spent all that time on writing vanilla HTML, CSS and JS and I would have had the same result in the same amount of time. I agree, if time would be all that mattered.</p>
<p>But for some people <em>(like me)</em>, feeling productive, seeing how easy it is to test my ideas and how code seems to flow from my fingertips at the speed of thought, is what decides if I’ll ever finish and publish something, or if I’ll lose my patience and fallback to comfort zones.</p>
<p>Having to write the same boilerplate code over and over again, constant context switching between files, jumping back into a project after a few days and not knowing where everything was in those thousand-lines files.. these are all detractors that will eventually make me say <em>”f••k this! at least my day job brings money”</em>.</p>
<h3 id="reusability">Reusability</h3>
<p>So many JS frameworks were created in the name of reusable components, but they all failed for me.</p>
<p>I mean sure, I can <em>“npm install”</em> a React calendar, and I am now <em>“reusing”</em> and not <em>“reimplementing”</em> the hard work of someone better than me at calendar UIs. But just try to stray away a little from the happy path that the component creator envisioned, and you will find it is mind-bendingly hard to bend the component to your specific needs.</p>
<p>You might raise a Github issue and the creator will add a few params so you can customize that specific thing, but so will others with different and maybe clashing needs. Soon enough, that component is declared unwieldy and too complex to use, the dev will say <a href="https://github.com/docker/cli/issues/267#issuecomment-695149477"  target="_blank" rel="noopener" ><em>“f••k this! I’d rather do furniture”</em></a> and someone else will come out and say: <strong>here’s the <em>next best thing</em> in React calendar libraries, so much simpler to use than those behemoths!</strong></p>
<p>I never had this goal in mind but unexpectedly, the above setup is generic enough that I was able to extract it into a set of files for starting a new website. I can now duplicate that folder and start changing site-specific bits to get a new website.</p>
<p>Here are the websites I’ve done using this method:</p>
<ul>
<li><a href="https://lowtechguys.com/"  target="_blank" rel="noopener" >lowtechguys.com</a> / where I publish my apps</li>
<li><a href="https://subsol.one/"  target="_blank" rel="noopener" >subsol.one</a> / app I made for my brother to publish parties on</li>
<li><a href="https://robert.panaitiu.com"  target="_blank" rel="noopener" >robert.panaitiu.com</a> / my brother’s personal website</li>
<li><a href="https://lunar.fyi/"  target="_blank" rel="noopener" >lunar.fyi</a> / the app I have been talking about above</li>
</ul>
<blockquote>
<p>And the best thing I remember is that for each website I published a working version, good looking enough, with a contact page and small bandwidth requirements, in less than a day.</p>
</blockquote>
<p>How does this solve the problem of straying away from the happy path? Well, this is not an immutable library residing in <em>node_modules</em>, or a JS script on a CDN. It is a set of files I can modify to the site’s needs.</p>
<p>There is no high wall to jump <em>(having to fork a library, figuring out its unique build system etc.)</em> or need to stick to a specific structure. Once the folder is duplicated, it  has its own life.</p>
<blockquote>
<p>For those interested, here is the repo containing the current state of my templates: <a href="https://github.com/alin23/plim-website/"  target="_blank" rel="noopener" >github.com/alin23/plim-website</a></p>
</blockquote>
<blockquote>
<p>I don&rsquo;t recommend using it, it&rsquo;s possible that I&rsquo;m the only one who finds it simple because I know what went into it. But if you do, I&rsquo;d love to <a href="/contact" >hear your thoughts</a>.</p>
</blockquote>
<h2 id="gatsby-jekyll-hugo">Gatsby? Jekyll? Hugo?</h2>
<p>Weirdly, this website I’m writing on is not made with Plim. At some point I decided to start a personal website, and I thought it probably needs a blog-aware site builder.</p>
<p>At the time, I didn&rsquo;t know that RSS is an easily templatable XML file, and that all I need for a blog is to write Markdown.</p>
<p>I remember trying <a href="https://www.gatsbyjs.com/"  target="_blank" rel="noopener" >Gatsby</a> and not liking the JS ecosystem around it. <a href="https://jekyllrb.com/"  target="_blank" rel="noopener" >Jekyll</a> was my second choice with Github Pages, but I think I fumbled too much with <code>ruby</code> and <code>bundle</code> to get it working and lost patience.</p>
<blockquote>
<p>Both problems stemmed from my lack of familiarity with their ecosystems, but my goal was to write a blog, not learn Ruby and JS.</p>
</blockquote>
<p><a href="https://gohugo.io/"  target="_blank" rel="noopener" >Hugo</a> seemed much simpler, and it was also written in Go and distributed as a standalone binary, which I always like for my tools.</p>
<p>I marveled at Hugo&rsquo;s speed, loved the fact that it supports theming (although it&rsquo;s not as simple as it sounds) and that it has a lot of useful stuff built-in like syntax highlighting, image processing, RSS generator etc. But it took me sooo long to understand its structure.</p>
<p>There are many foreign words <em>(to me)</em> in Hugo: <em>archetypes</em>, <em>taxonomies</em>, <em>shortcodes</em>, <em>partials</em>, <em>layouts</em>, <em>categories</em>, <em>series</em>. Unfortunately, by the time I realized that I don’t need the flexibility that this structure provides, I had already finished this website and written my first article.</p>
<p>I also used <a href="https://blogophonic-hugo.netlify.app/"  target="_blank" rel="noopener" >a theme</a> that uses the Tachyons CSS framework, for which I can never remember the <a href="https://tachyons.io/docs/table-of-styles/"  target="_blank" rel="noopener" >right class to use</a>. I thought about rewriting the website in Plim but converting everything to Tailwind or simple CSS would have been a lot of work.</p>
<p>I eventually started writing simple Markdown files for <a href="https://notes.alinpanaitiu.com/How%20I%20write%20this%20blog%20on%20my%20iPhone%20in%20a%20train"  target="_blank" rel="noopener" >my notes</a>, and have Caddy convert and serve them on the fly. Helps me write from my phone and not have to deal with Git and Hugo.</p>
<p>I still keep this for longform content, where a laptop is usually needed anyway.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>Reverse engineering the MacBook clamshell mode</title>
      <link>https://alinpanaitiu.com/blog/turn-off-macbook-display-clamshell/</link>
      <pubDate>Tue, 17 Jan 2023 15:16:13 +0200</pubDate>
      <author>alin@panaitiu.com (Alin Panaitiu)</author>
      <guid>https://alinpanaitiu.com/blog/turn-off-macbook-display-clamshell/</guid>
      <description>
        <![CDATA[<p>You just got a large, Ultrawide monitor for your MacBook. You hook it up and marvel at the amount of pixels.</p>
<p>You notice you never use the MacBook built-in display anymore, and it nags you to have it in your lower peripheral vision.</p>
<p>Closing the lid is not an option because you still use the keyboard and trackpad, maybe even the webcam and TouchID from time to time. So you try things:</p>
<ul>
<li>you try turning off the display by lowering the brightness completely. 🤔 hmm ok but now:
<ul>
<li>your mouse wanders to that screen sometimes</li>
<li>some windows get lost in there</li>
<li>..and you still waste GPU cycles for rendering 6 million unused pixels</li>
</ul>
</li>
<li>you mirror the monitor to the built-in screen. nice, this solves the first two issues!
<ul>
<li><em>ok but why did the resolution change? do I have to change it back every time I do this??</em></li>
<li><em>wait, why don&rsquo;t I get notifications anymore?! oh. there&rsquo;s <a href="https://alinpanaitiu.comimages/notifications-mirroring.png" >a setting for that</a></em></li>
</ul>
</li>
<li>you walk away from the desk, the screen goes to sleep</li>
<li>you come back, the screen is now on something like 6% brightness, not completely off anymore
<ul>
<li><em>ok, press <code>Brightness Down</code> again, I can live with that</em></li>
<li><em>oh, mirroring got disabled as well.. at least there&rsquo;s <code>Cmd</code>+<code>Brightness Down</code></em></li>
</ul>
</li>
</ul>
<p>Why isn&rsquo;t there a way to actually disable this screen?</p>
<h2 id="blackouthttpslunarfyiblackout"><a href="https://lunar.fyi/#blackout"  target="_blank" rel="noopener" >BlackOut</a></h2>
<p>Because a lot of users of my <strong><a href="https://lunar.fyi/"  target="_blank" rel="noopener" >🌕 Lunar</a></strong> app told me about their grievances with not being able to turn off individual displays in software, I went down the rabbit hole of display mirroring and automated all of the above.</p>














<a href="https://alinpanaitiu.comimages/blackout-ui.png">
  <figure>
    <figcaption>Lunar interface showing the BlackOut function</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/blackout-ui.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/blackout-ui.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/blackout-ui.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/blackout-ui.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/blackout-ui.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/blackout-ui.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/blackout-ui.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/blackout-ui.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/blackout-ui.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/blackout-ui.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/blackout-ui.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/blackout-ui.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/blackout-ui.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/blackout-ui.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/blackout-ui.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/blackout-ui.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/blackout-ui.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/blackout-ui.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/blackout-ui.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/blackout-ui.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/blackout-ui.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/blackout-ui.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/blackout-ui.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/blackout-ui.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/blackout-ui.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/blackout-ui.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/blackout-ui.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/blackout-ui.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/blackout-ui.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/blackout-ui.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/blackout-ui.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/blackout-ui.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/blackout-ui.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/blackout-ui.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/blackout-ui.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/blackout-ui.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/blackout-ui.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/blackout-ui.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/blackout-ui.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/blackout-ui.png"
        alt="Lunar interface showing the BlackOut function"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>Now someone can turn off and on any display at will using keyboard shortcuts, and can even automate the above <strong>MacBook</strong> + <strong>monitor</strong> workflow to trigger when an external monitor gets connected and disconnected.</p>
<p>But it&rsquo;s still nagging me that somehow macOS can actually disable the internal screen completely, but we&rsquo;re stuck with this <em>zero-brightness-mirroring</em> abomination.</p>
<h2 id="clamshell-mode">Clamshell Mode</h2>
<p>When closing the MacBook lid while a monitor is still connected, the internal screen disappears from the screen list and the external monitors remain available.</p>
<p>This function is called <em>clamshell mode</em> in the laptop world. Congratulations, your $3000 all-in-one computer is now just an SoC with some USB-C ports. Ok, you also get the speakers and the inefficient cooling system.</p>














<a href="https://alinpanaitiu.comimages/macbook-clamshell-on-stand.jpg">
  <figure>
    <figcaption>MacBook with lid closed sitting vertically on a wooden stand</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/macbook-clamshell-on-stand.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/macbook-clamshell-on-stand.jpg"
        alt="MacBook with lid closed sitting vertically on a wooden stand"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>In the pre-chunky-MacBook-Pro-with-notch era, the lid was detected as being closed using magnets in the lid, and some <a href="https://guide-images.cdn.ifixit.com/igi/TWIoJgROcI65mq4A.full"  target="_blank" rel="noopener" >hall effect sensors</a>. So you were able to trick macOS into thinking the lid was closed by simply placing two powerful magnets at its sides.</p>
<p>With the new 2021 design, the MacBook has a <a href="https://www.ifixit.com/News/33952/apple-put-a-hinge-sensor-in-the-16-macbook-pro-what-could-it-be-for"  target="_blank" rel="noopener" >hinge sensor</a>, that can detect not only if the lid is closed, but also the angle of its closing. Magnets can&rsquo;t trick&rsquo;em anymore.</p>
<p>But all these sensors will probably just trigger some event in software, where a handler will decide if the display should be disabled or not, and call some <code>disableScreenInClamshellMode</code> function.</p>
<p>So where is that function, and can we call it ourselves?</p>
<h2 id="the-software-side">The software side</h2>
<p>Since Apple Silicon, most userspace code lives in a single file called a DYLD Shared Cache. Since Ventura, that is located in a <a href="https://eclecticlight.co/2022/11/16/cryptex-how-a-custom-iphone-is-changing-macos-updates/"  target="_blank" rel="noopener" >Cryptex</a> (a read-only volume) at the following path:</p>
<p><code>/System/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_arm64e</code></p>
<p>Since that file is mostly an optimised concatenation of macOS Frameworks, we can extract the binaries using <a href="https://github.com/keith/dyld-shared-cache-extractor"  target="_blank" rel="noopener" >keith/dyld-shared-cache-extractor</a>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">mkdir -p ~/Temp/dyld <span class="o">&amp;&amp;</span> <span class="nb">cd</span> ~/Temp/dyld
</span></span><span class="line"><span class="cl">dyld-shared-cache-extractor /System/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_arm64e <span class="nv">$PWD</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Let&rsquo;s extract the exported and unexported symbols in text format to be able to search them easily using something like <a href="https://github.com/BurntSushi/ripgrep"  target="_blank" rel="noopener" >ripgrep</a>.</p>
<p>I&rsquo;m using <code>/usr/bin/nm</code> with <a href="https://github.com/sharkdp/fd"  target="_blank" rel="noopener" >fd</a>&rsquo;s <code>-x</code> option to take advantage of parallelisation. I like its syntax more than <code>parallel</code>&rsquo;s since it has integrated interpolation for the basename/dirname of the argument <em>(note the <code>{/}</code>)</em></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl"><span class="nf">mkdir</span> symbols private-symbols
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">fd</span> <span class="na">--maxdepth</span> <span class="m">1</span> <span class="na">-t</span> f <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    . ./System/Library/*Frameworks/*.framework/Versions/A/ <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="na">-x</span> sh <span class="na">-c</span> <span class="s1">&#39;nm --demangle --defined-only --extern-only {} &gt; symbols/{/}&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nf">fd</span> <span class="na">--maxdepth</span> <span class="m">1</span> <span class="na">-t</span> f <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    . ./System/Library/*Frameworks/*.framework/Versions/A/ <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="na">-x</span> sh <span class="na">-c</span> <span class="s1">&#39;nm --demangle --defined-only {} &gt; private-symbols/{/}&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Searching for <code>clamshell</code> gives us interesting results. The most notable is this one inside SkyLight:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl">~/Temp/dyld ❯ rg <span class="na">-i</span> clamshell
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">symbols</span>/SkyLight
</span></span><span class="line"><span class="cl"><span class="nf">1710</span>:00000001d44bce70 S _kSLSDisplayControlRequestClamshellState
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>SkyLight.framework</code> is what handles window and display management in macOS and it usually exports enough symbols that we can use from Swift so I&rsquo;m inclined to follow this path.</p>
<p>Let&rsquo;s see if the internet has anything for us. I usually search for code on <a href="https://sourcegraph.com"  target="_blank" rel="noopener" >SourceGraph</a> as it has indexed some large macOS repos with dyld dumps. Looking for  <code>RequestClamshellState</code> gives us <a href="https://sourcegraph.com/github.com/apple-oss-distributions/PowerManagement/-/blob/pmconfigd/PMDisplay.m?L527:6&amp;subtree=true"  target="_blank" rel="noopener" >something far more interesting</a> though:</p>














<a href="https://alinpanaitiu.comimages/sourcegraph-requestclamshellstate.png">
  <figure>
    <figcaption>searching RequestClamshellState on SourceGraph</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sourcegraph-requestclamshellstate.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/sourcegraph-requestclamshellstate.png"
        alt="searching RequestClamshellState on SourceGraph"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<hr>
<p>Looks like Apple open sourced the power management code, nice! It even has recent ARM64 code in there, are we that lucky?</p>
<p>Here&rsquo;s an excerpt of something relevant to our cause:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="n">SLSDisplayPowerControlClient</span> <span class="o">*</span><span class="n">gSLPowerClient</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">kPMClamshellOpen</span>          <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">kPMClamshellClosed</span>        <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">kPMClamshellUnknown</span>       <span class="o">=</span> <span class="mi">3</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">kPMClamshellDoesNotExist</span>  <span class="o">=</span> <span class="mi">4</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">handleSkylightCheckIn</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// create ws power control client
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">NSError</span> <span class="o">*</span><span class="n">err</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">gSLPowerClient</span> <span class="o">=</span> <span class="p">[[</span><span class="n">SLSDisplayPowerControlClient</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initAsyncPowerControlClient</span><span class="p">:</span><span class="o">&amp;</span><span class="n">err</span> <span class="nl">notifyQueue</span><span class="p">:</span><span class="n">_getPMMainQueue</span><span class="p">()</span> <span class="nl">notificationType</span><span class="p">:</span><span class="n">kSLDCNotificationTypeNone</span> <span class="nl">notificationBlock</span><span class="p">:</span><span class="o">^</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">dict</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">dict</span> <span class="o">!=</span> <span class="nb">nil</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">handleSkylightNotification</span><span class="p">(</span><span class="n">dict</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">ERROR_LOG</span><span class="p">(</span><span class="s">&#34;Received a nil dictionary from WindowServer callback&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}];</span>
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">requestClamshellState</span><span class="p">(</span><span class="n">SLSClamshellState</span> <span class="n">state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* Forward clamshell state to WindowServer
</span></span></span><span class="line"><span class="cl"><span class="cm">     A) a request with a clamshell state of close in interpreted as a turn off clamshell display (clamshell close)
</span></span></span><span class="line"><span class="cl"><span class="cm">     B) a request with a clamshell state of open in interpreted as a turn on internal and ANY external displays (clamshell open)
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">gSLCheckIn</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">ERROR_LOG</span><span class="p">(</span><span class="s">&#34;WindowServer has not checked in. Refusing to change clamshell display state&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">NSError</span> <span class="o">*</span><span class="n">err</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">NSMutableDictionary</span> <span class="o">*</span><span class="n">request</span> <span class="o">=</span> <span class="p">[[</span><span class="n">NSMutableDictionary</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithCapacity</span><span class="p">:</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">NSNumber</span> <span class="o">*</span><span class="n">ns_state</span> <span class="o">=</span> <span class="p">[[</span><span class="n">NSNumber</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithUnsignedChar</span><span class="p">:</span><span class="n">state</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span><span class="n">request</span> <span class="nl">setValue</span><span class="p">:</span><span class="n">ns_state</span> <span class="nl">forKey</span><span class="p">:</span><span class="n">kSLSDisplayControlRequestClamshellState</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">SLSDisplayControlRequestUUID</span> <span class="n">uuid</span> <span class="o">=</span> <span class="p">[</span><span class="n">gSLPowerClient</span> <span class="nl">requestStateChange</span><span class="p">:(</span><span class="n">NSDictionary</span> <span class="o">*</span><span class="k">const</span><span class="p">)</span><span class="n">request</span> <span class="nl">error</span><span class="p">:</span><span class="o">&amp;</span><span class="n">err</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">([</span><span class="n">err</span> <span class="n">code</span><span class="p">]</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">       <span class="n">ERROR_LOG</span><span class="p">(</span><span class="s">&#34;Clamshell requestStateChange returned error %{public}@&#34;</span><span class="p">,</span> <span class="n">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">       <span class="n">INFO_LOG</span><span class="p">(</span><span class="s">&#34;requestClamshellState: state %u, Received uuid %llu&#34;</span><span class="p">,</span> <span class="n">state</span><span class="p">,</span> <span class="n">uuid</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">struct</span> <span class="n">request_entry</span> <span class="o">*</span><span class="n">entry</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">request_entry</span> <span class="o">*</span><span class="p">)</span><span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">request_entry</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="n">entry</span><span class="o">-&gt;</span><span class="n">uuid</span> <span class="o">=</span> <span class="n">uuid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">entry</span><span class="o">-&gt;</span><span class="n">valid</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">STAILQ_INSERT_TAIL</span><span class="p">(</span><span class="o">&amp;</span><span class="n">gRequestUUIDs</span><span class="p">,</span> <span class="n">entry</span><span class="p">,</span> <span class="n">entries</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">request</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">       <span class="p">[</span><span class="n">ns_state</span> <span class="k">release</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">       <span class="p">[</span><span class="n">request</span> <span class="k">release</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">[</span><span class="n">err</span> <span class="k">release</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>So it&rsquo;s instantiating an <code>SLSDisplayPowerControlClient</code> then calling its <code>requestStateChange</code> method. <code>SLS</code> is a prefix related to SkyLight <em>(probably standing for SkyLightServer)</em>, let&rsquo;s see if we have that code in our version of the framework.</p>
<p>I prefer to do that using <a href="https://www.hopperapp.com/"  target="_blank" rel="noopener" >Hopper</a> and its <strong>Read File From DYLD Cache</strong> feature which can extract a framework from the currently in-use cache:</p>














<a href="https://alinpanaitiu.comimages/hopper-read-file-dyld-cache.jpg">
  <figure>
    <figcaption>Hopper menu item showing Read file from DYLD cache</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hopper-read-file-dyld-cache.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/hopper-read-file-dyld-cache.jpg"
        alt="Hopper menu item showing Read file from DYLD cache"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>















<a href="https://alinpanaitiu.comimages/hopper-skylight-displaypowercontrol.png">
  <figure>
    <figcaption>Hopper showing SLSDisplayPowerControlClient</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 500px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 500px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 500px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hopper-skylight-displaypowercontrol.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/hopper-skylight-displaypowercontrol.png"
        alt="Hopper showing SLSDisplayPowerControlClient"
        
        
        style="max-width: 500px"
      >
    </picture>
  </figure>
</a>

<p>Ok the class and methods are there, let&rsquo;s look for what uses them. Since it&rsquo;s most likely a daemon handling power management, I&rsquo;ll look for it in <code>/System/Library</code>.</p>
<p>And looks like <code>powerd</code> is what we&rsquo;re looking for, containing exactly the code that we saw on SourceGraph.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl">❯ rg <span class="na">-uuu</span> requestClamshellState /System/Library/ <span class="m">2</span><span class="o">&gt;</span>/dev/null
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">/System/Library/CoreServices/powerd.bundle/powerd: binary file matches <span class="o">(</span><span class="nf">found</span> <span class="s2">&#34;\0&#34;</span> byte around offset <span class="nv">4</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">❯ hopperv4 <span class="na">-e</span> /System/Library/CoreServices/powerd.bundle/powerd
</span></span></code></pre></td></tr></table>
</div>
</div>













<a href="https://alinpanaitiu.comimages/hopper-requeststatechange.png">
  <figure>
    <figcaption>Hopper pseudocode calling requestStateChange</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hopper-requeststatechange.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/hopper-requeststatechange.png"
        alt="Hopper pseudocode calling requestStateChange"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<h2 id="writing-the-code">Writing the code</h2>
<p>To link and use <code>SLSDisplayPowerControlClient</code> we need some headers, as Swift doesn&rsquo;t have the method signatures available.</p>
<p>Looking for <code>SLSDisplayPowerControlClient</code> on SourceGraph gives us <a href="https://sourcegraph.com/github.com/cmsj/ApplePrivateHeaders/-/blob/macOS/11.3/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight/SLSDisplayPowerControlClient.h?L10:26&amp;subtree=true"  target="_blank" rel="noopener" >more than we need</a>.</p>
<p>Let&rsquo;s create a bridging header so that Swift can link to Objective-C symbols, and a Swift file to where we&rsquo;ll try to replicate what <code>powerd</code> does.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">mkdir clamshell <span class="o">&amp;&amp;</span> <span class="nb">cd</span> clamshell
</span></span><span class="line"><span class="cl">touch Bridging-Header.h Clamshell.swift
</span></span></code></pre></td></tr></table>
</div>
</div><h5 id="bridging-headerh">Bridging-Header.h</h5>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="cp">#import &lt;Foundation/Foundation.h&gt;
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="k">@interface</span> <span class="nc">SLSDisplayPowerControlClient</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span> <span class="p">(</span><span class="kt">id</span><span class="p">)</span><span class="nf">initAsyncPowerControlClient:</span><span class="p">(</span><span class="kt">id</span><span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">notifyQueue:</span><span class="p">(</span><span class="kt">id</span><span class="p">)</span><span class="nv">arg2</span> <span class="nf">notificationType:</span><span class="p">(</span><span class="kt">UInt8</span><span class="p">)</span><span class="nv">arg3</span> <span class="nf">notificationBlock:</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSDictionary</span><span class="o">*</span><span class="p">))</span><span class="nv">notificationBlock</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">-</span> <span class="p">(</span><span class="kt">id</span><span class="p">)</span><span class="nf">initPowerControlClient:</span><span class="p">(</span><span class="kt">id</span><span class="o">*</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">notifyQueue:</span><span class="p">(</span><span class="kt">id</span><span class="p">)</span><span class="nv">arg2</span> <span class="nf">notificationType:</span><span class="p">(</span><span class="kt">UInt8</span><span class="p">)</span><span class="nv">arg3</span> <span class="nf">notificationBlock:</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">^</span><span class="p">)(</span><span class="n">NSDictionary</span><span class="o">*</span><span class="p">))</span><span class="nv">notificationBlock</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">-</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">)</span><span class="nf">requestStateChange:</span><span class="p">(</span><span class="kt">id</span><span class="p">)</span><span class="nv">arg1</span> <span class="nf">error:</span><span class="p">(</span><span class="kt">id</span><span class="o">*</span><span class="p">)</span><span class="nv">arg2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">@end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="n">NSString</span><span class="o">*</span> <span class="n">kSLSDisplayControlRequestClamshellState</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kt">UInt8</span> <span class="n">kSLDCNotificationTypeNone</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h5 id="clamshellswift">Clamshell.swift</h5>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">import</span> <span class="nc">Foundation</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">enum</span> <span class="nc">ClamshellState</span><span class="p">:</span> <span class="nb">Int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">open</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">closed</span> <span class="p">=</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">unknown</span> <span class="p">=</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">doesNotExist</span> <span class="p">=</span> <span class="mi">4</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nv">err</span><span class="p">:</span> <span class="nb">AnyObject</span><span class="p">?</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">skyLightPowerClient</span> <span class="p">=</span> <span class="n">SLSDisplayPowerControlClient</span><span class="p">(</span><span class="n">powerControlClient</span><span class="p">:</span> <span class="p">&amp;</span><span class="n">err</span><span class="p">,</span> <span class="n">notifyQueue</span><span class="p">:</span> <span class="n">DispatchQueue</span><span class="p">.</span><span class="n">main</span><span class="p">,</span> <span class="n">notificationType</span><span class="p">:</span> <span class="n">kSLDCNotificationTypeNone</span><span class="p">)</span> <span class="p">{</span> <span class="n">dict</span> <span class="k">in</span>
</span></span><span class="line"><span class="cl">    <span class="bp">print</span><span class="p">(</span><span class="n">dict</span> <span class="k">as</span> <span class="nb">Any</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">requestClamshellState</span><span class="p">(</span><span class="kc">_</span> <span class="n">state</span><span class="p">:</span> <span class="n">ClamshellState</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Send the request</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">request</span><span class="p">:</span> <span class="p">[</span><span class="n">AnyHashable</span><span class="p">:</span> <span class="nb">Any</span><span class="p">]</span> <span class="p">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="n">kSLSDisplayControlRequestClamshellState</span><span class="p">:</span> <span class="n">NSNumber</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="n">state</span><span class="p">.</span><span class="n">rawValue</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nv">err</span><span class="p">:</span> <span class="nb">AnyObject</span><span class="p">?</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">uuid</span> <span class="p">=</span> <span class="n">skyLightPowerClient</span><span class="p">!.</span><span class="n">requestStateChange</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">error</span><span class="p">:</span> <span class="p">&amp;</span><span class="n">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Check the response</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">err</span> <span class="k">as</span><span class="p">!</span> <span class="n">NSError</span><span class="p">?)?.</span><span class="n">code</span> <span class="o">!=</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="bp">print</span><span class="p">(</span><span class="s">&#34;Clamshell requestStateChange returned error&#34;</span><span class="p">,</span> <span class="n">err</span><span class="p">?.</span><span class="n">localizedDescription</span> <span class="p">??</span> <span class="s">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="bp">print</span><span class="p">(</span><span class="s">&#34;requestClamshellState: state %u, Received uuid %llu&#34;</span><span class="p">,</span> <span class="n">state</span><span class="p">,</span> <span class="n">uuid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="bp">print</span><span class="p">(</span><span class="n">skyLightPowerClient</span><span class="p">!)</span>
</span></span><span class="line"><span class="cl"><span class="n">requestClamshellState</span><span class="p">(.</span><span class="n">closed</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h3 id="compiling">Compiling&hellip;</h3>
<p>To compile the binary using <code>swiftc</code> we have to point it to the location of SkyLight.framework which is located at <code>/System/Library/PrivateFrameworks</code>.</p>
<p>We then tell it to link the framework using <code>-framework SkyLight</code> and import our bridging header. Then we run the resulting binary.</p>
<p><em>I prefer to run this using <code>entr</code> to watch the files for changes. With the code editor on the left and the terminal on the right, I can iterate and try things faster by just editing and saving the file, then watch the output on the right.</em></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">swiftc <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -F/System/Library/PrivateFrameworks <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -framework SkyLight <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -import-objc-header Bridging-Header.h <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    Clamshell.swift -o Clamshell
</span></span><span class="line"><span class="cl">./Clamshell
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># For faster iteration, watch file changes with entr:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> Clamshell.swift Bridging-Header.h <span class="p">|</span> entr -rs <span class="s1">&#39;\
</span></span></span><span class="line"><span class="cl"><span class="s1">	swiftc -F/System/Library/PrivateFrameworks \
</span></span></span><span class="line"><span class="cl"><span class="s1">		-framework SkyLight \
</span></span></span><span class="line"><span class="cl"><span class="s1">		-import-objc-header Bridging-Header.h \
</span></span></span><span class="line"><span class="cl"><span class="s1">		 Clamshell.swift -o Clamshell \
</span></span></span><span class="line"><span class="cl"><span class="s1">	&amp;&amp; ./Clamshell&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Well.. it&rsquo;s not working. The error is not helpful at all, there&rsquo;s nothing on the internet related to it.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="n">SLSDisplayPowerControlClient</span><span class="p">:</span> <span class="mh">0x600000fb8720</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="n">Clamshell</span> <span class="n">requestStateChange</span> <span class="n">returned</span> <span class="n">error</span> <span class="n">The</span> <span class="n">operation</span> <span class="n">couldn</span><span class="err">’</span><span class="n">t</span> <span class="n">be</span> <span class="n">completed</span><span class="p">.</span> <span class="p">(</span><span class="n">CoreGraphicsErrorDomain</span> <span class="n">error</span> <span class="mf">1004.</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="looking-for-errors">Looking for errors</h3>
<p>Maybe the system log has something for us. One can check that using Console.app but I prefer looking at it in the Terminal through the <code>/usr/bin/log</code> utility.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">log stream --predicate <span class="s1">&#39;eventMessage contains &#34;Clamshell&#34;&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Something from AMFI about the binary signature. CMS stands for <strong><a href="https://medium.com/csit-tech-blog/demystifying-ios-code-signature-309d52c2ff1d"  target="_blank" rel="noopener" >Cryptographic Message Syntax</a></strong> which is what <code>codesign</code> adds to a binary when it signs it with a certificate.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">kernel</span><span class="p">:</span> <span class="p">(</span><span class="n">AppleMobileFileIntegrity</span><span class="p">)</span> <span class="n">AMFI</span><span class="p">:</span> <span class="err">&#39;</span><span class="o">/</span><span class="n">Users</span><span class="o">/</span><span class="n">alin</span><span class="o">/</span><span class="n">Temp</span><span class="o">/</span><span class="n">dyld</span><span class="o">/</span><span class="n">clamshell</span><span class="o">/</span><span class="n">Clamshell</span><span class="err">&#39;</span> <span class="n">has</span> <span class="n">no</span> <span class="n">CMS</span> <span class="n">blob</span><span class="p">?</span>
</span></span><span class="line"><span class="cl"><span class="n">kernel</span><span class="p">:</span> <span class="p">(</span><span class="n">AppleMobileFileIntegrity</span><span class="p">)</span> <span class="n">AMFI</span><span class="p">:</span> <span class="err">&#39;</span><span class="o">/</span><span class="n">Users</span><span class="o">/</span><span class="n">alin</span><span class="o">/</span><span class="n">Temp</span><span class="o">/</span><span class="n">dyld</span><span class="o">/</span><span class="n">clamshell</span><span class="o">/</span><span class="n">Clamshell</span><span class="err">&#39;</span><span class="p">:</span> <span class="n">Unrecoverable</span> <span class="n">CT</span> <span class="n">signature</span> <span class="n">issue</span><span class="p">,</span> <span class="n">bailing</span> <span class="n">out</span><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="n">tccd</span><span class="p">:</span> <span class="p">[</span><span class="n">com</span><span class="p">.</span><span class="n">apple</span><span class="p">.</span><span class="n">TCC</span><span class="p">:</span><span class="n">access</span><span class="p">]</span> <span class="n">AUTHREQ_ATTRIBUTION</span><span class="p">:</span> <span class="n">msgID</span><span class="p">=</span><span class="mf">60667.1</span><span class="p">,</span> <span class="n">attribution</span><span class="p">={</span><span class="n">responsible</span><span class="p">={</span><span class="n">TCCDProcess</span><span class="p">:</span> <span class="n">identifier</span><span class="p">=</span><span class="n">kitty</span><span class="p">,</span> <span class="n">pid</span><span class="p">=</span><span class="mi">19959</span><span class="p">,</span> <span class="n">auid</span><span class="p">=</span><span class="mi">501</span><span class="p">,</span> <span class="n">euid</span><span class="p">=</span><span class="mi">501</span><span class="p">,</span> <span class="n">responsible_path</span><span class="p">=</span><span class="o">/</span><span class="n">Applications</span><span class="o">/</span><span class="n">kitty</span><span class="p">.</span><span class="n">app</span><span class="o">/</span><span class="n">Contents</span><span class="o">/</span><span class="n">MacOS</span><span class="o">/</span><span class="n">kitty</span><span class="p">,</span> <span class="n">binary_path</span><span class="p">=</span><span class="o">/</span><span class="n">Applications</span><span class="o">/</span><span class="n">kitty</span><span class="p">.</span><span class="n">app</span><span class="o">/</span><span class="n">Contents</span><span class="o">/</span><span class="n">MacOS</span><span class="o">/</span><span class="n">kitty</span><span class="p">},</span> <span class="n">requesting</span><span class="p">={</span><span class="n">TCCDProcess</span><span class="p">:</span> <span class="n">identifier</span><span class="p">=</span><span class="n">Clamshell</span><span class="p">,</span> <span class="n">pid</span><span class="p">=</span><span class="mi">60667</span><span class="p">,</span> <span class="n">auid</span><span class="p">=</span><span class="mi">501</span><span class="p">,</span> <span class="n">euid</span><span class="p">=</span><span class="mi">501</span><span class="p">,</span> <span class="n">binary_path</span><span class="p">=</span><span class="o">/</span><span class="n">Users</span><span class="o">/</span><span class="n">alin</span><span class="o">/</span><span class="n">Temp</span><span class="o">/</span><span class="n">dyld</span><span class="o">/</span><span class="n">clamshell</span><span class="o">/</span><span class="n">Clamshell</span><span class="p">},</span> <span class="p">},</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>I have GateKeeper disabled and running the binary from a terminal that&rsquo;s added to the special Developer Tools section of <strong>Security &amp; Privacy</strong>, so this shouldn&rsquo;t cause any problems.</p>
<p>I checked just to be sure, and signing it with my $100/year Apple Developer certificate gets rid of the <code>CMS blob</code> error but doesn&rsquo;t change anything in the result.</p>
<hr>


<div class="flex items-center justify-between flex-column flex-row-ns">
    <div class="mw6">
        <h3>Phew, let's take a break</h3>
        <p class="mt4 f5">I just arrived <small><i>after a long train ride</i></small> at the house I'm rebuilding with my wife, and wanted to share this nice view with you 😌</p>
        <p class="f5">It's January, but the sun is warming our faces and the hazelnut trees are already producing their <a href="https://gardenplannerwebsites.azureedge.net/blog/catkins-2x.jpg">yellow catkins</a>.</p>
        <p class="mt4 f5">Ten years ago, the children of the house's previous owners were walking in knee deep snow and coasting downhill on their wooden sleds, hurting a few young fir trees on the way down. 🌲</p>
        <p class="f5 mb4">Seasons are changing.</p>
    </div>
    <img class="ma0" src="https://alinpanaitiu.comimages/breaza-sun-in-january.jpg" alt="breaza sun in January" height=400>
</div>

<hr>
<h2 id="digging-deeper">Digging deeper</h2>
<p>Some system capabilities can only be accessed if the binary has been signed by Apple and has specific <a href="https://secret.club/2020/08/14/macos-entitlements.html"  target="_blank" rel="noopener" >entitlements</a>. Checking for <code>powerd</code>&rsquo;s entitlements gives us something worrying.</p>
<p>The binary seems to use <code>com.apple.private.*</code> entitlements. This usually means that some APIs will fail if the required entitlements are not present.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl"><span class="o">&gt;</span> codesign <span class="na">-d</span> <span class="na">--entitlements</span> <span class="na">- </span>/System/Library/CoreServices/powerd.bundle/powerd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">Executable</span><span class="o">=</span>/System/Library/CoreServices/powerd.bundle/powerd
</span></span><span class="line"><span class="cl"><span class="o">[</span>Dict<span class="o">]</span>
</span></span><span class="line"><span class="cl">	...
</span></span><span class="line"><span class="cl">	<span class="o">[</span>Key<span class="o">]</span> com.apple.private.SkyLight.displaypowercontrol
</span></span><span class="line"><span class="cl">	<span class="o">[</span>Value<span class="o">]</span>
</span></span><span class="line"><span class="cl">		<span class="o">[</span>Bool<span class="o">]</span> true
</span></span><span class="line"><span class="cl">	...
</span></span></code></pre></td></tr></table>
</div>
</div><p>We can try to add the entitlements ourselves. We just need to create a plist file and use it in <code>codesign</code>:</p>
<h5 id="entitlementsplist">Entitlements.plist</h5>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE plist PUBLIC &#34;-//Apple//DTD PLIST 1.0//EN&#34; &#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd&#34;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;plist</span> <span class="na">version=</span><span class="s">&#34;1.0&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;dict&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;key&gt;</span>com.apple.private.SkyLight.displaypowercontrol<span class="nt">&lt;/key&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;true/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/dict&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/plist&gt;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Sign the binary with entitlements and run it:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">❯ codesign -fs <span class="nv">$CODESIGN_CERT</span> --entitlements Entitlements.plist Clamshell
</span></span><span class="line"><span class="cl">❯ ./Clamshell
</span></span><span class="line"><span class="cl">Job 1, <span class="s1">&#39;./Clamshell&#39;</span> terminated by signal SIGKILL <span class="o">(</span>Forced quit<span class="o">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Looks like we&rsquo;re getting killed instantly. The log stream shows AMFI is doing that because we&rsquo;re not Apple and we&rsquo;re not supposed to use that entitlement.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">kernel</span><span class="p">:</span> <span class="n">mac_vnode_check_signature</span><span class="p">:</span> <span class="o">/</span><span class="n">Users</span><span class="o">/</span><span class="n">alin</span><span class="o">/</span><span class="n">Temp</span><span class="o">/</span><span class="n">dyld</span><span class="o">/</span><span class="n">clamshell</span><span class="o">/</span><span class="n">Clamshell</span><span class="p">:</span> <span class="n">code</span> <span class="n">signature</span> <span class="n">validation</span> <span class="n">failed</span> <span class="n">fatally</span><span class="p">:</span> <span class="n">When</span> <span class="n">validating</span> <span class="o">/</span><span class="n">Users</span><span class="o">/</span><span class="n">alin</span><span class="o">/</span><span class="n">Temp</span><span class="o">/</span><span class="n">dyld</span><span class="o">/</span><span class="n">clamshell</span><span class="o">/</span><span class="n">Clamshell</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="n">Code</span> <span class="n">has</span> <span class="n">restricted</span> <span class="n">entitlements</span><span class="p">,</span> <span class="n">but</span> <span class="n">the</span> <span class="n">validation</span> <span class="n">of</span> <span class="n">its</span> <span class="n">code</span> <span class="n">signature</span> <span class="n">failed</span><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="n">Unsatisfied</span> <span class="n">Entitlements</span><span class="p">:</span> <span class="n">com</span><span class="p">.</span><span class="n">apple</span><span class="p">.</span><span class="kd">private</span><span class="p">.</span><span class="n">SkyLight</span><span class="p">.</span><span class="n">displaypowercontrol</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">kernel</span><span class="p">:</span> <span class="p">(</span><span class="n">AppleSystemPolicy</span><span class="p">)</span> <span class="n">ASP</span><span class="p">:</span> <span class="n">Security</span> <span class="n">policy</span> <span class="n">would</span> <span class="n">not</span> <span class="n">allow</span> <span class="n">process</span><span class="p">:</span> <span class="mi">57234</span><span class="p">,</span> <span class="o">/</span><span class="n">Users</span><span class="o">/</span><span class="n">alin</span><span class="o">/</span><span class="n">Temp</span><span class="o">/</span><span class="n">dyld</span><span class="o">/</span><span class="n">clamshell</span><span class="o">/</span><span class="n">Clamshell</span>
</span></span><span class="line"><span class="cl"><span class="n">amfid</span><span class="p">:</span> <span class="o">/</span><span class="n">Users</span><span class="o">/</span><span class="n">alin</span><span class="o">/</span><span class="n">Temp</span><span class="o">/</span><span class="n">dyld</span><span class="o">/</span><span class="n">clamshell</span><span class="o">/</span><span class="n">Clamshell</span> <span class="n">not</span> <span class="n">valid</span><span class="p">:</span> <span class="n">Error</span> <span class="n">Domain</span><span class="p">=</span><span class="n">AppleMobileFileIntegrityError</span> <span class="n">Code</span><span class="p">=</span><span class="o">-</span><span class="mi">413</span> <span class="s">&#34;No matching profile found&#34;</span> <span class="n">UserInfo</span><span class="p">={</span><span class="n">NSURL</span><span class="p">=</span><span class="n">file</span><span class="p">:</span><span class="c1">///Users/alin/Temp/dyld/clamshell/Clamshell, unsatisfiedEntitlements=&lt;CFArray 0x155e1b600 [0x1f0e613a8]&gt;{type = immutable, count = 1, values = (</span>
</span></span><span class="line"><span class="cl">    <span class="mi">0</span> <span class="p">:</span> <span class="p">&lt;</span><span class="n">CFString</span> <span class="mh">0x155e12db0</span> <span class="p">[</span><span class="mh">0x1f0e613a8</span><span class="p">]</span><span class="o">&gt;</span><span class="p">{</span><span class="n">contents</span> <span class="p">=</span> <span class="s">&#34;com.apple.private.SkyLight.displaypowercontrol&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">)},</span> <span class="n">NSLocalizedDescription</span><span class="p">=</span><span class="n">No</span> <span class="n">matching</span> <span class="n">profile</span> <span class="n">found</span><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="amfi">AMFI</h3>
<p>What&rsquo;s this AMFI exactly and why is it telling us what we can and cannot do on our own device?</p>
<p>The acronym stands for <a href="https://eclecticlight.co/2018/12/29/amfi-checking-file-integrity-on-your-mac/"  target="_blank" rel="noopener" >Apple Mobile File Integrity</a> and it&rsquo;s the process enforcing code signature at the system level.</p>
<p>By default, the OS locks these private APIs because if we would be able to use them, a malware or a bad actor would be able to do it as well. With it locked by default, malware authors are deterred from trying to use these APIs on targets of lower importance as this would usually need a 0-day exploit.</p>
<p>In the end it&rsquo;s just another layer of security, and if in the rare case someone needs to bypass it, Apple provides a way to do it. The process involves disabling <strong>System Integrity Protection</strong> and adding <code>amfi_get_out_of_my_way=1</code> as a boot arg.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl"><span class="c"># Inside a Recovery terminal (to disable SIP)
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> csrutil disable
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> reboot
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># Inside a normal terminal after disabling SIP
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> sudo <span class="nf">nvram</span> boot-<span class="nv">args</span><span class="o">=</span><span class="s2">&#34;amfi_get_out_of_my_way=1&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> sudo <span class="nf">reboot</span> now
</span></span></code></pre></td></tr></table>
</div>
</div><p>I don&rsquo;t recommend doing this as it puts you at great risk, since the system volume is no longer read only, and code signatures are no longer enforced.</p>
<p>I only keep this state for research that I do in short periods of time, then turn SIP back on for normal day to day usage.</p>
<p>In case you need to revert the above changes:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl"><span class="c"># Inside a normal terminal before enabling SIP
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> sudo <span class="nf">nvram</span> boot-<span class="nv">args</span><span class="o">=</span><span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># Inside a Recovery terminal (to enable SIP)
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> csrutil enable
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> reboot
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h3 id="no-more-amfi">No more AMFI?</h3>
<p>Unfortunately even after disabling AMFI, we&rsquo;re still encountering the <code>CoreGraphicsError 1004</code>. It&rsquo;s true, AMFI is not complaining about the entitlements anymore, they&rsquo;re accepted and the binary is not <code>SIGKILL</code>ed.</p>
<p>But we still can&rsquo;t get into clamshell mode using just software.</p>
<h2 id="fridahttpsfridare"><a href="https://frida.re/"  target="_blank" rel="noopener" >Frida</a></h2>
<p>If you haven&rsquo;t heard of it, Frida is this awesome tool that lets you inject code into already running processes, hook functions by name <em>(or even by address)</em>, observe how and when they&rsquo;re called, check their arguments and even make your own calls.</p>
<p>Let me share with you another macOS boot arg that I like:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl"><span class="nf">sudo</span> <span class="nf">nvram</span> boot-<span class="nv">args</span><span class="o">=</span>-arm64e_preview_abi
</span></span></code></pre></td></tr></table>
</div>
</div><p>This one enables code injection. Now we can use Frida to hook the SkyLight power control methods to see how they are called as we close and open the lid:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl"><span class="o">&gt;</span> sudo <span class="nf">frida-trace</span> <span class="na">-t</span> SkyLight <span class="na">-m</span> <span class="s1">&#39;-[SLSDisplayPowerControlClient *]&#39;</span> powerd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// Closing the lid
</span></span><span class="line"><span class="cl">           /* TID 0x5427 */
</span></span><span class="line"><span class="cl">  <span class="nf">4617</span> ms  SLSDisplayControlRequestClamshellStateKey: <span class="m">2</span>
</span></span><span class="line"><span class="cl">  <span class="nf">4617</span> ms  <span class="na">-[SLSDisplayPowerControlClient</span> requestStateChange:0x13cf06a60 error:0x16b8ca828<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">4628</span> ms     <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> service<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">4628</span> ms     <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> sendStateChangeRequest:0x13cf06a60 uuid:0x16b8ca7e0<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">4628</span> ms     <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> service<span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// Opening the lid
</span></span><span class="line"><span class="cl">           /* TID 0x8a17 */
</span></span><span class="line"><span class="cl"> <span class="nf">10537</span> ms  SLSDisplayControlRequestClamshellStateKey: <span class="m">1</span>
</span></span><span class="line"><span class="cl"> <span class="nf">10537</span> ms  <span class="na">-[SLSDisplayPowerControlClient</span> requestStateChange:0x13cc1e1c0 error:0x16b9567a8<span class="o">]</span>
</span></span><span class="line"><span class="cl"> <span class="nf">10538</span> ms     <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> service<span class="o">]</span>
</span></span><span class="line"><span class="cl"> <span class="nf">10538</span> ms     <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> sendStateChangeRequest:0x13cc1e1c0 uuid:0x16b956760<span class="o">]</span>
</span></span><span class="line"><span class="cl"> <span class="nf">10538</span> ms     <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> service<span class="o">]</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>We got our confirmation at least. <code>powerd</code> is indeed calling <code>SLSDisplayPowerControlClient.requestStateChange(2)</code> when closing the lid.</p>
<p>Let&rsquo;s check what happens when we try to call that method in <code>Clamshell.swift</code>.</p>
<p>We first add the line <code>readLine(strippingNewline: true)</code> at the top of the <code>Clamshell.swift</code> file to make the binary wait for us to press <code>Enter</code>. This is so that we have a running process that we can attach to with Frida.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl"><span class="o">&gt;</span> sudo <span class="nf">frida-trace</span> <span class="na">-t</span> SkyLight <span class="na">-m</span> <span class="s1">&#39;-[SLSDisplayPowerControlClient *]&#39;</span> Clamshell
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">           /* TID 0x103 */
</span></span><span class="line"><span class="cl">  <span class="nf">1475</span> ms  SLSDisplayControlRequestClamshellStateKey: <span class="m">2</span>
</span></span><span class="line"><span class="cl">  <span class="nf">1475</span> ms  <span class="na">-[SLSDisplayPowerControlClient</span> requestStateChange:0x600001d64510 error:0x16d8d3c90<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">1479</span> ms     <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> service<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">1479</span> ms     <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> sendStateChangeRequest:0x600001d64510 uuid:0x16d8d3a10<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">1479</span> ms     <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> service<span class="o">]</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Everything looks the same, seems that we&rsquo;re not looking deep enough.</p>
<p>The request method seems to access the <code>service</code> property which is an <code>SLSXPCService</code>. <a href="https://developer.apple.com/documentation/xpc"  target="_blank" rel="noopener" >XPC Services</a> are what macOS uses for low-level interprocess communication.</p>
<p>A process can expose an XPC Service using a label (e.g. <code>com.myapp.RemoteControlService</code>) and listen to requests coming through, other processes can connect to it using the same label and send requests.</p>
<p>The system handles the routing part. And the authentication part.</p>
<p>Looks like an XPC Service can also be <a href="https://developer.apple.com/documentation/foundation/nsxpclistener/3943310-setconnectioncodesigningrequirem"  target="_blank" rel="noopener" >restricted to specific code signing requirements</a>, is it possible that this is what we&rsquo;re running into here?</p>
<p>Let&rsquo;s trace <code>SLSXPCService</code> methods as well using Frida:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl"><span class="o">&gt;</span> sudo <span class="nf">frida-trace</span> <span class="na">-t</span> SkyLight <span class="na">-m</span> <span class="s1">&#39;-[SLSDisplayPowerControlClient *]&#39;</span> <span class="na">-m</span> <span class="s1">&#39;-[SLSXPCService *]&#39;</span> powerd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// Closing the lid while observing powerd
</span></span><span class="line"><span class="cl">           /* TID 0x518b */
</span></span><span class="line"><span class="cl">  <span class="nf">3029</span> ms  <span class="na">-[SLSDisplayPowerControlClient</span> requestStateChange:0x139621c60 error:0x16f0c2828<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">3029</span> ms  SLSDisplayControlRequestClamshellStateKey: <span class="m">2</span>
</span></span><span class="line"><span class="cl">  <span class="nf">3043</span> ms     <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> service<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">3043</span> ms     <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> sendStateChangeRequest:0x139621c60 uuid:0x16f0c27e0<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">3043</span> ms     <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> service<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">3043</span> ms     <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSXPCService</span> sendXPCDictionary:0x13a913be0<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">3043</span> ms     <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSXPCService</span> reinitConnection<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">3043</span> ms     <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSXPCService</span> enabled<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">3043</span> ms     <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSXPCService</span> enabled<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">3043</span> ms     <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSXPCService</span> connected<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">3043</span> ms     <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSXPCService</span> connection<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">3452</span> ms  <span class="na">-[SLSXPCService</span> handleXPCEvent:0x13ad0ea40<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">3452</span> ms     <span class="o">|</span> <span class="na">-[SLSXPCService</span> enabled<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">3452</span> ms     <span class="o">|</span> <span class="na">-[SLSXPCService</span> cfStringToCStringPtr:0x1f3133020<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">3452</span> ms     <span class="o">|</span> <span class="na">-[SLSXPCService</span> connected<span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">&gt;</span> sudo <span class="nf">frida-trace</span> <span class="na">-t</span> SkyLight <span class="na">-m</span> <span class="s1">&#39;-[SLSDisplayPowerControlClient *]&#39;</span> <span class="na">-m</span> <span class="s1">&#39;-[SLSXPCService *]&#39;</span> Clamshell
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// Trying to send the clamshell request in software
</span></span><span class="line"><span class="cl">  <span class="nf">1435</span> ms  <span class="na">-[SLSDisplayPowerControlClient</span> requestStateChange:0x6000014d4030 error:0x16b123c90<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">1435</span> ms  SLSDisplayControlRequestClamshellStateKey: <span class="m">2</span>
</span></span><span class="line"><span class="cl">  <span class="nf">1444</span> ms     <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> service<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">1444</span> ms     <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> sendStateChangeRequest:0x6000014d4030 uuid:0x16b123a10<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">1444</span> ms     <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSDisplayPowerControlClient</span> service<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">1444</span> ms     <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSXPCService</span> sendXPCDictionary:0x600003ec4000<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">1444</span> ms     <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSXPCService</span> reinitConnection<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">1444</span> ms     <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSXPCService</span> enabled<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">1444</span> ms     <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSXPCService</span> connected<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">1444</span> ms     <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSXPCService</span> autoreconnect<span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="nf">1444</span> ms     <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSXPCService</span> enabled<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="nf">Process</span> terminated
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// ...we&#39;re missing this stuff
</span></span><span class="line"><span class="cl">//  <span class="m">3043</span> ms     <span class="o">|</span>    <span class="o">|</span>    <span class="o">|</span> <span class="na">-[SLSXPCService</span> connection<span class="o">]</span>
</span></span><span class="line"><span class="cl">//  <span class="m">3452</span> ms  <span class="na">-[SLSXPCService</span> handleXPCEvent:0x13ad0ea40<span class="o">]</span>
</span></span><span class="line"><span class="cl">//  <span class="m">3452</span> ms     <span class="o">|</span> <span class="na">-[SLSXPCService</span> enabled<span class="o">]</span>
</span></span><span class="line"><span class="cl">//  <span class="m">3452</span> ms     <span class="o">|</span> <span class="na">-[SLSXPCService</span> cfStringToCStringPtr:0x1f3133020<span class="o">]</span>
</span></span><span class="line"><span class="cl">//  <span class="m">3452</span> ms     <span class="o">|</span> <span class="na">-[SLSXPCService</span> connected<span class="o">]</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Great! or not?</p>
<p>I&rsquo;m not sure if I should be happy that we found that our clamshell request doesn&rsquo;t work because we don&rsquo;t have an XPC connection, or if I should be worried that this means we won&rsquo;t be able to make this work with SIP enabled.</p>
<p>I guess it&rsquo;s time to go deeper to find out.</p>
<h2 id="xpc-services">XPC Services</h2>
<p>Now that we have access to Frida, we can use the handy <a href="https://github.com/hot3eed/xpcspy"  target="_blank" rel="noopener" >xpcspy</a> tool to sniff the XPC communication of <code>powerd</code>.</p>
<p>I&rsquo;m thinking maybe we can find the endpoint name of the XPC listener and just connect to it and send a raw message directly, instead of relying on SkyLight to do that.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-fish" data-lang="fish"><span class="line"><span class="cl"><span class="o">&gt;</span> sudo <span class="nf">xpcspy</span> <span class="na">--parse</span> powerd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// Closing the lid
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">xpc_connection_send_message</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span>OS_xpc_connection: <span class="o">&lt;</span>connection: 0x13a808820<span class="o">&gt;</span> <span class="o">{</span> <span class="nv">name</span> <span class="o">=</span> <span class="o">(</span><span class="nf">anonymous</span><span class="o">)</span>, <span class="nv">listener</span> <span class="o">=</span> false, <span class="nv">pid</span> <span class="o">=</span> <span class="m">30630</span>, <span class="nv">euid</span> <span class="o">=</span> <span class="m">88</span>, <span class="nv">egid</span> <span class="o">=</span> <span class="m">88</span>, <span class="nv">asid</span> <span class="o">=</span> <span class="m">100014</span> <span class="o">}&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span>OS_xpc_dictionary<span class="o">&gt;</span> <span class="o">{</span> <span class="nv">count</span> <span class="o">=</span> <span class="m">4</span> <span class="nv">contents</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;PayloadType&#34;</span> <span class="o">=&gt;</span> <span class="o">&lt;</span>OS_xpc_uint64: <span class="o">&lt;</span>uint64: 0x81917509705f5717<span class="o">&gt;</span>: <span class="m">3</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;Command&#34;</span> <span class="o">=&gt;</span> <span class="o">&lt;</span>OS_xpc_uint64: <span class="o">&lt;</span>uint64: 0x81917509705f572f<span class="o">&gt;</span>: <span class="m">4</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;Payload&#34;</span> <span class="o">=&gt;</span> <span class="o">&lt;</span>data<span class="o">&gt;</span> <span class="o">{</span> <span class="nv">length</span> <span class="o">=</span> <span class="m">92</span> bytes, <span class="nv">contents</span> <span class="o">=</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">	    <span class="nv">SLSDisplayControlRequestClamshellStateKey</span> <span class="o">=</span> <span class="m">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="o">}</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;UUID&#34;</span> <span class="o">=&gt;</span> <span class="o">&lt;</span>OS_xpc_uint64: <span class="o">&lt;</span>uint64: 0x81917509705f5647<span class="o">&gt;</span>: <span class="m">41</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>So we have <code>name = (anonymous), listener = false, pid = 30630</code>.</p>
<p>An anonymous listener, can it get even worse? The PID coincides with <code>WindowServer --daemon</code> so it&rsquo;s definitely the message we&rsquo;re also trying to send.  But with an anonymous listener, we&rsquo;re stuck to relying on SkyLight&rsquo;s exported code to reach it.</p>
<p>I guess we need to go back to do some old-school assembly reading.</p>
<hr>
<h3 id="filling-empty-spaces">Filling empty spaces</h3>
<p>After renaming some sub-procedures in Hopper, looking at the graph reveals the different code paths that <code>powerd</code> and <code>Clamshell</code> are taking through <code>SLSXPCService.reinitConnection</code>.</p>
<h4 id="powerd">powerd</h4>
<ol>
<li>sees that the service&rsquo;s <code>enabled</code> and <code>connected</code> properties are <code>true</code></li>
<li>so it gets out of <code>reinitConnection</code></li>
<li>and straight into sending the XPC dictionary through the available <code>connection</code>.</li>
</ol>
<h4 id="clamshell">Clamshell</h4>
<ul>
<li>sees that <code>enabled</code>, <code>connected</code> and <code>autoreconnect</code> are <code>false</code>
<ul>
<li>so it fails with a <code>CGError</code></li>
</ul>
</li>
<li>if those properties were <code>true</code> it would go on the right-side code path which
<ul>
<li>checks if properties at <code>0x20</code> and <code>0x28</code> are non-zero</li>
<li>then proceeds to reconnect.</li>
</ul>
</li>
</ul>














<a href="https://alinpanaitiu.comimages/hopper-trace-reinitconnection.png">
  <figure>
    <figcaption>Hopper graph showing reinitConnection</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hopper-trace-reinitconnection.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/hopper-trace-reinitconnection.png"
        alt="Hopper graph showing reinitConnection"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>Adding some <code>Memory.readPointer</code> calls inside <code>__handlers__/SLSXPCService/reinitConnection.js</code> shows us what SkyLight is expecting to see at <code>0x20</code> and <code>0x28</code>:</p>
<p>Two <code>NSMallocBlock</code>s right after the <code>OS_xpc_connection</code> and the <code>OS_dispatch_queue_serial</code> properties.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl">  <span class="mi">5502</span> <span class="n">ms</span>   <span class="o">-</span><span class="p">[</span><span class="n">SLSXPCService</span> <span class="n">reinitConnection</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="mi">5502</span> <span class="n">ms</span>       <span class="n">arg0</span> <span class="n">obj</span><span class="p">:</span> <span class="p">&lt;</span><span class="n">SLSXPCService</span><span class="p">:</span> <span class="mh">0x11f50f130</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="mi">5502</span> <span class="n">ms</span>       <span class="n">Memory</span><span class="p">.</span><span class="n">readPointer</span> <span class="mh">0x8</span> <span class="mh">0x101</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="mi">5502</span> <span class="n">ms</span>       <span class="n">Memory</span><span class="p">.</span><span class="n">readPointer</span> <span class="mh">0x10</span> <span class="mh">0x11f50bb10</span>
</span></span><span class="line"><span class="cl">  <span class="mi">5502</span> <span class="n">ms</span>       <span class="n">SLSXPCService</span> <span class="n">at</span> <span class="mh">0x10</span> <span class="p">&lt;</span><span class="n">OS_xpc_connection</span><span class="p">:</span> <span class="p">&lt;</span><span class="n">connection</span><span class="p">:</span> <span class="mh">0x11f50bb10</span><span class="p">&gt;</span> <span class="p">{</span> <span class="n">name</span> <span class="p">=</span> <span class="p">(</span><span class="n">anonymous</span><span class="p">),</span> <span class="n">listener</span> <span class="p">=</span> <span class="kc">false</span><span class="p">,</span> <span class="n">pid</span> <span class="p">=</span> <span class="mi">396</span><span class="p">,</span> <span class="n">euid</span> <span class="p">=</span> <span class="mi">88</span><span class="p">,</span> <span class="n">egid</span> <span class="p">=</span> <span class="mi">88</span><span class="p">,</span> <span class="n">asid</span> <span class="p">=</span> <span class="mi">100014</span> <span class="p">}</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="mi">5502</span> <span class="n">ms</span>       <span class="n">Memory</span><span class="p">.</span><span class="n">readPointer</span> <span class="mh">0x18</span> <span class="mh">0x11df05970</span>
</span></span><span class="line"><span class="cl">  <span class="mi">5502</span> <span class="n">ms</span>       <span class="n">SLSXPCService</span> <span class="n">at</span> <span class="mh">0x18</span> <span class="p">&lt;</span><span class="n">OS_dispatch_queue_serial</span><span class="p">:</span> <span class="n">Power</span> <span class="n">Management</span> <span class="n">main</span> <span class="n">queue</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="mi">5502</span> <span class="n">ms</span>       <span class="n">Memory</span><span class="p">.</span><span class="n">readPointer</span> <span class="mh">0x20</span> <span class="mh">0x11f50a740</span>
</span></span><span class="line"><span class="cl">  <span class="mi">5502</span> <span class="n">ms</span>       <span class="n">SLSXPCService</span> <span class="n">at</span> <span class="mh">0x20</span> <span class="p">&lt;</span><span class="n">__NSMallocBlock__</span><span class="p">:</span> <span class="mh">0x11f50a740</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="mi">5502</span> <span class="n">ms</span>       <span class="n">Memory</span><span class="p">.</span><span class="n">readPointer</span> <span class="mh">0x28</span> <span class="mh">0x11f50a770</span>
</span></span><span class="line"><span class="cl">  <span class="mi">5502</span> <span class="n">ms</span>       <span class="n">SLSXPCService</span> <span class="n">at</span> <span class="mh">0x28</span> <span class="p">&lt;</span><span class="n">__NSMallocBlock__</span><span class="p">:</span> <span class="mh">0x11f50a770</span><span class="p">&gt;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Judging by the contents of <a href="https://sourcegraph.com/github.com/cmsj/ApplePrivateHeaders/-/blob/macOS/11.3/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight/SLSXPCService.h?L22-23"  target="_blank" rel="noopener" >SLSXPCService.h</a>, those are the closures for <code>errorBlock</code> and <code>notificationBlock</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="k">@interface</span> <span class="nc">SLSXPCService</span> : <span class="nc">NSObject</span> <span class="o">&lt;</span><span class="n">SLSXPCServiceProtocol</span><span class="o">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kt">char</span> <span class="n">_enabled</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">char</span> <span class="n">_connected</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">char</span> <span class="n">_setTarget</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="kt">char</span> <span class="n">_autoreconnect</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="n">NSObject</span><span class="o">*&lt;</span><span class="n">OS_xpc_object</span><span class="o">&gt;</span> <span class="n">_connection</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="n">NSObject</span><span class="o">*&lt;</span><span class="n">OS_dispatch_queue</span><span class="o">&gt;</span> <span class="n">_notifyQueue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="cm">/* This would be 0x20 */</span> <span class="kt">id</span> <span class="n">_errorBlock</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="cm">/* This would be 0x28 */</span> <span class="kt">id</span> <span class="n">_notificationBlock</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="cm">/*^block*/</span><span class="kt">id</span> <span class="n">_clientErrorBlock</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="cm">/*^block*/</span><span class="kt">id</span> <span class="n">_clientNotificationBlock</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>I&rsquo;m inching closer to the good code path but I seem to never get there.</p>
<p>So here&rsquo;s what I did so far in <code>Clamshell.swift</code> before calling <code>requestClamshellState</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="k">guard</span> <span class="kd">let</span> <span class="nv">service</span> <span class="p">=</span> <span class="n">skyLightPowerClient</span><span class="p">.</span><span class="n">service</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="bp">print</span><span class="p">(</span><span class="s">&#34;SLSXPCService is nil&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">service</span><span class="p">.</span><span class="n">autoreconnect</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="n">service</span><span class="p">.</span><span class="n">errorBlock</span> <span class="p">=</span> <span class="p">{</span> <span class="n">err</span> <span class="k">in</span>
</span></span><span class="line"><span class="cl">    <span class="bp">print</span><span class="p">(</span><span class="s">&#34;service.errorBlock&#34;</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">service</span><span class="p">.</span><span class="n">notificationBlock</span> <span class="p">=</span> <span class="p">{</span> <span class="n">notification</span> <span class="k">in</span>
</span></span><span class="line"><span class="cl">    <span class="bp">print</span><span class="p">(</span><span class="s">&#34;service.notificationBlock&#34;</span><span class="p">,</span> <span class="n">notification</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>After calling <code>requestClamshellState</code>, the code crashes with <code>SIGSEGV</code> inside <code>createNoSenderRecvPairWithQueue:errorHandler:eventHandler:</code> because it branches to the <code>0x0</code> address.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-asm" data-lang="asm"><span class="line"><span class="cl"><span class="err">//</span> <span class="nf">The</span> <span class="no">crashing</span> <span class="no">instruction</span> <span class="no">is</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="nf">ldr</span>    <span class="no">x8</span><span class="p">,</span> <span class="p">[</span><span class="no">x19</span><span class="p">,</span> <span class="c">#0x28]
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="err">//</span> <span class="nf">Because</span> <span class="no">the</span> <span class="no">memory</span> <span class="no">at</span> <span class="p">[</span><span class="no">x19</span><span class="p">,</span> <span class="c">#0x28] contains 0x0
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="err">//</span> <span class="nf">And</span> <span class="no">register</span> <span class="no">x19</span> <span class="no">contains</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"><span class="err">(</span><span class="nf">lldb</span><span class="p">)</span> <span class="no">po</span> <span class="no">$x19</span>
</span></span><span class="line"><span class="cl"><span class="err">&lt;</span><span class="nl">__NSMallocBlock__:</span> <span class="err">0</span><span class="nf">x600000c08330</span><span class="err">&gt;</span>
</span></span><span class="line"><span class="cl"> <span class="nl">signature:</span> <span class="err">&#34;</span><span class="nf">v8@</span><span class="err">?</span><span class="mi">0</span><span class="err">&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="nf">invoke</span>   <span class="p">:</span> <span class="mi">0x1a081a958</span> <span class="p">(</span><span class="err">/</span><span class="no">System</span><span class="err">/</span><span class="no">Library</span><span class="err">/</span><span class="no">PrivateFrameworks</span><span class="err">/</span><span class="no">SkyLight.framework</span><span class="err">/</span><span class="no">Versions</span><span class="err">/</span><span class="no">A</span><span class="err">/</span><span class="no">SkyLight</span><span class="err">`</span><span class="no">__75-</span><span class="p">[</span><span class="no">SLSXPCService</span> <span class="no">createNoSenderRecvPairWithQueue</span><span class="p">:</span><span class="no">errorHandler</span><span class="p">:</span><span class="no">eventHandler</span><span class="p">:]</span><span class="no">_block_invoke</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nf">copy</span>     <span class="p">:</span> <span class="mi">0x1a05e0a18</span> <span class="p">(</span><span class="err">/</span><span class="no">System</span><span class="err">/</span><span class="no">Library</span><span class="err">/</span><span class="no">PrivateFrameworks</span><span class="err">/</span><span class="no">SkyLight.framework</span><span class="err">/</span><span class="no">Versions</span><span class="err">/</span><span class="no">A</span><span class="err">/</span><span class="no">SkyLight</span><span class="err">`</span><span class="no">__copy_helper_block_e8_32b40r</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nf">dispose</span>  <span class="p">:</span> <span class="mi">0x1a05e09d4</span> <span class="p">(</span><span class="err">/</span><span class="no">System</span><span class="err">/</span><span class="no">Library</span><span class="err">/</span><span class="no">PrivateFrameworks</span><span class="err">/</span><span class="no">SkyLight.framework</span><span class="err">/</span><span class="no">Versions</span><span class="err">/</span><span class="no">A</span><span class="err">/</span><span class="no">SkyLight</span><span class="err">`</span><span class="no">__destroy_helper_block_e8_32b40r</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="giving-up-for-now">Giving up (for now)</h3>
<p>Unfortunately I&rsquo;m a bit lost here. I&rsquo;ll take a break and hope that the solution comes in a dream or on a long walk like in those mythical stories.</p>
<p>The article is already longer than I&rsquo;d be inclined to read so if anyone reaches this point, congrats, you have the patience of a monk.</p>
<p>If there are better ways to approach a problem like this one, I&rsquo;d be glad to hear about it through <a href="/contact" >the contact form</a>.</p>
<p>I&rsquo;m not always happy to learn that I&rsquo;ve wasted 4 days on a problem that could have been solved in a few hours with the right tools, but at least I&rsquo;ll learn how not to bore people with writings on rudimentary tasks next time.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>A window switcher on the Mac App Store? Is it even possible?</title>
      <link>https://alinpanaitiu.com/blog/window-switcher-app-store/</link>
      <pubDate>Tue, 02 Aug 2022 21:01:04 +0300</pubDate>
      <author>alin@panaitiu.com (Alin Panaitiu)</author>
      <guid>https://alinpanaitiu.com/blog/window-switcher-app-store/</guid>
      <description>
        <![CDATA[<blockquote>
<p><em>Not really, no. Not without annoying workarounds and a confusing user experience.</em></p>
</blockquote>
<p>Another email, another annoyed user: <em>Firefox not loading websites when launched through rcmd! It works when launched from Alfred.. Please fix ASAP!!</em> I’m gonna fix this Firefox issue once and for all!</p>
<p>Launch Xcode, open the <strong>


<img
    src="https://alinpanaitiu.comimages/icons/rcmd_16@1x.webp#x16"
    alt="rcmd icon"
    
    
     height="16"
/> <a href="https://lowtechguys.com/rcmd"  target="_blank" rel="noopener" >rcmd</a></strong> project, check the <code>launchApp</code> function code, it’s just a <code>NSWorkspace.open</code> call on Firefox.app, what does Alfred do differently?</p>
<p>Disassemble Alfred.app in Hopper, look for <code>NSWorkspace.open</code>, of course it’s there, it’s the exact same thing.</p>














<a href="https://alinpanaitiu.comimages/alfred-hopper.png">
  <figure>
    <figcaption>screenshot of hopper showing where open is used in Alfred code</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/alfred-hopper.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/alfred-hopper.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/alfred-hopper.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/alfred-hopper.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/alfred-hopper.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/alfred-hopper.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/alfred-hopper.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/alfred-hopper.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/alfred-hopper.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/alfred-hopper.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/alfred-hopper.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/alfred-hopper.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/alfred-hopper.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/alfred-hopper.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/alfred-hopper.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/alfred-hopper.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/alfred-hopper.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/alfred-hopper.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/alfred-hopper.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/alfred-hopper.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/alfred-hopper.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/alfred-hopper.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/alfred-hopper.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/alfred-hopper.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/alfred-hopper.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/alfred-hopper.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/alfred-hopper.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/alfred-hopper.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/alfred-hopper.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/alfred-hopper.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/alfred-hopper.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/alfred-hopper.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/alfred-hopper.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/alfred-hopper.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/alfred-hopper.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/alfred-hopper.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/alfred-hopper.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/alfred-hopper.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/alfred-hopper.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/alfred-hopper.png"
        alt="screenshot of hopper showing where open is used in Alfred code"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>Try <code>open /Applications/Firefox.app</code> in a terminal, it works, websites load as expected.</p>
<p>Breakpoint on <code>launchApp</code>, check the debugger again, let’s be rigorous, what am I really calling <code>open</code> on?</p>
<p>Argument is <code>/System/Volumes/Data/Applications/Firefox.app</code> which is just a symlink to <code>/Applications/Firefox.app</code> right? .. or was it the other way around? Anyway let’s just try it for the sake of it, I’m desperate.</p>
<p>Run <code>open /System/Volumes/Data/Applications/Firefox.app</code>, huh?? no websites load? THAT WAS IT?!</p>
<p>Add <code>path.replacingOccurrences(of: &quot;/System/Volumes/Data&quot;, with: &quot;&quot;)</code>, build, run, hold <code>Right Command</code>, press <code>F</code>, Firefox launches and holy cow everything works!!</p>
<p>I don’t even care why anymore, let’s just release this fix on the App Store.</p>
<p>And while I’m at it, why not try to add that window switching capability that people have been asking about?</p>
<p>I remember something about Accessibility permissions not being available in the sandbox, but I just used an App Store app that was able to request the permissions so there has to be a way, how hard could it be?</p>
<p><em><strong>Well it turns out it’s pretty darn hard, and I’m still working on this window switching thing to this day.. sigh.. let me tell you about it.</strong></em></p>
<hr>
<h2 id="apps-vs-windows">Apps vs windows</h2>
<p>There’s an important distinction between switching windows and switching apps on the Mac. As opposed to Microsoft Windows where you just <code>Alt-Tab</code> through .. well, <strong>windows</strong>, on macOS you <code>Command Tab</code> through <strong>apps</strong> by default. When an app with multiple windows is focused, <code>Command backtick</code> will cycle through the windows of that app.</p>














<a href="https://alinpanaitiu.comimages/keyboard-cmd-tab-backtick.png">
  <figure>
    <figcaption>keyboard with command tab and backtick keys highlighted</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/keyboard-cmd-tab-backtick.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/keyboard-cmd-tab-backtick.png"
        alt="keyboard with command tab and backtick keys highlighted"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>Six years ago I was a Windows power user, and when I got my first Mac, Command Tabbing through apps felt very weird. Suddenly I was closing all windows of <strong>


<img
    src="https://alinpanaitiu.comimages/icons/sublime_16@1x.webp#x16"
    alt="sublime text icon"
    
    
     height="16"
/> Sublime</strong> but its icon was still there in the Command Tab list, or I would minimize <strong>


<img
    src="https://alinpanaitiu.comimages/icons/chrome_16@1x.webp#x16"
    alt="chrome icon"
    
    
     height="16"
/> Chrome</strong> and focusing its icon didn’t unminimize it. The app vs window distinction just didn’t exist in my mind.</p>
<p>Now, after 6 years, the macOS way feels a lot more intuitive:</p>
<ul>
<li>I mostly switch between apps with a single window (browser, terminal etc.)</li>
<li>There’s a very small subset of apps where I might have more than one window (code editor, image/PDF viewer)</li>
<li>I might want to keep my code editor app running even after I closed all its windows, so I can have it load instantly when opening a new window to edit a file/project</li>
<li>Minimizing windows that are irrelevant at the moment allows me to cycle through the relevant ones with <code>Command backtick</code></li>
<li>No need to see a thumbnail of each window, when almost all apps are single window</li>
</ul>
<p>Of course it might just be the power of habit, after all I was able to be just as productive with the Windows way in the past ¯\_(ツ)_/¯</p>
<h2 id="command-tab-tab-tab-tab">Command Tab Tab Tab Tab…</h2>
<p>The app centric approach is nice but having to switch between 10 different apps at a time gets annoying fast.</p>


<div style="width: 100%; display: flex; justify-content: center; align-items: center;">
    <video disablepictureinpicture autoplay loop muted playsinline preload="none" style="width: 100%; max-width: 400px; margin: 10px auto; border-radius: 8px;" poster="https://alinpanaitiu.comimages/cmdtab-frustrating-fast.webp" src="/video/cmdtab-frustrating-fast.mp4">
    </video>
</div>

<p><em>Pressing Tab 5 times in a row to get to the app I want could be categorized as a first world problem and I should just get used to it.  But doing that 50 times a day and having to always visually check if I chose the right icon, tends to break my flow of thinking, and makes me get tired faster because of all the context switching.</em></p>
<p>That’s the main reason I created <strong>


<img
    src="https://alinpanaitiu.comimages/icons/rcmd_16@1x.webp#x16"
    alt="rcmd icon"
    
    
     height="16"
/> <a href="https://lowtechguys.com/rcmd"  target="_blank" rel="noopener" >rcmd</a></strong>, to switch apps without thinking about switching apps.</p>
<p>My right thumb rests nicely on the <code>Right Command</code> key and I barely use that easy to reach key. So I turned it into a dedicated app switching key.</p>
<h3 id="dynamic-assignments">Dynamic assignments</h3>
<p>I decided to dynamically assign each app the first letter of its name so that I don’t have to try to remember <em>what key did I assign to Xcode?</em>. I just hold <code>Right Command</code> and press <code>X</code> without any mental effort because I know I have no other app starting with <code>X</code>.</p>
<p>And if I forgot that <strong>


<img
    src="https://alinpanaitiu.comimages/icons/xcode_16@1x.webp#x16"
    alt="Xcode icon"
    
    
     height="16"
/> Xcode</strong> is not already running <em>(or if it crashes in the background like it sometimes does)</em>, rcmd launches it automatically <em>(since I clearly wanted it running if I tried to focus it)</em>.</p>
<h3 id="static-assignments">Static assignments</h3>
<p>Xcode is a happy case though. I have so many apps starting with <code>S</code> that I decided custom assignments might be a better fit for that. I left Sublime Text for the <code>S</code> key since it’s my most used app, and then assigned mnemonic keys for others:</p>
<ul>
<li><code>O</code> for 


<img
    src="https://alinpanaitiu.comimages/icons/soulver_16@1x.webp#x16"
    alt="soulver app icon"
    
    
     height="16"
/> S<strong>o</strong>ulver</li>
<li><code>P</code> for 


<img
    src="https://alinpanaitiu.comimages/icons/spotify_16@1x.webp#x16"
    alt="spotify app icon"
    
    
     height="16"
/> S<strong>p</strong>otify</li>
<li><code>E</code> for 


<img
    src="https://alinpanaitiu.comimages/icons/sketch_16@1x.webp#x16"
    alt="sketch app icon"
    
    
     height="16"
/> Sk<strong>e</strong>tch <em>(because <code>K</code> is taken by the 


<img
    src="https://alinpanaitiu.comimages/icons/kitty_16@1x.webp#x16"
    alt="kitty app icon"
    
    
     height="16"
/> Kitty terminal)</em></li>
<li><code>B</code> for 


<img
    src="https://alinpanaitiu.comimages/icons/safari_16@1x.webp#x16"
    alt="safari app icon"
    
    
     height="16"
/> Safari <strong>b</strong>rowser</li>
<li>Other rarely used apps <em>(SF Symbols, Slack, Sublime Merge)</em> will be reachable by cycling using <code>rcmd-rshift-s</code> <em>(it’s good enough for me as I rarely have those open)</em></li>
</ul>
<h3 id="seek-and-hide">Seek and hide</h3>
<p>Often I need to check the status of an app briefly and then get back to what I was doing. Some examples</p>
<ul>
<li>


<img
    src="https://alinpanaitiu.comimages/icons/kitty_16@1x.webp#x16"
    alt="kitty app icon"
    
    
     height="16"
/>  check a long running task in the terminal</li>
<li>


<img
    src="https://alinpanaitiu.comimages/icons/mail_16@1x.webp#x16"
    alt="mail app icon"
    
    
     height="16"
/>  check if I got an email I’m waiting for while notifications are paused</li>
<li>


<img
    src="https://alinpanaitiu.comimages/icons/spotify_16@1x.webp#x16"
    alt="spotify app icon"
    
    
     height="16"
/>  see what’s this dope song that started playing from my Discover Weekly playlist</li>
</ul>
<p>That’s why I added the <strong>Hide</strong> action in rcmd.</p>
<p>Now I just hold <code>Right Command</code> and press <code>K</code> to check the <strong>


<img
    src="https://alinpanaitiu.comimages/icons/kitty_16@1x.webp#x16"
    alt="kitty app icon"
    
    
     height="16"
/> <a href="https://sw.kovidgoyal.net/kitty/"  target="_blank" rel="noopener" >Kitty</a></strong> terminal, then, without lifting any finger, press <code>K</code> again to hide it and get back to what I was doing.</p>
<p><em>This also allows the system to activate <strong>App Nap</strong> for the hidden app and put it into a lower energy usage state until I need it again.</em></p>


<div style="width: 100%; display: flex; justify-content: center; align-items: center; flex-direction: column;">
    <video disablepictureinpicture autoplay loop muted playsinline preload="none" style="width: 100%; max-width: 782px; margin: 10px auto; border-radius: 12px;" poster="https://alinpanaitiu.comimages/rcmd-demo-macbook.webp" src="/video/rcmd-demo-macbook.mp4">
    </video>
    <em>Using rcmd on my MacBook Pro 14"</em>
</div>

<hr>
<h2 id="is-window-switching-even-needed">Is window switching even needed?</h2>
<p>Unfortunately yes, there are many cases where an app might have a lot of windows open:</p>
<ul>
<li>


<img
    src="https://alinpanaitiu.comimages/icons/sublime_16@1x.webp#x16"
    alt="sublime text icon"
    
    
     height="16"
/>  Separate projects/folders open in Sublime Text</li>
<li>


<img
    src="https://alinpanaitiu.comimages/icons/pages_16@1x.webp#x16"
    alt="pages icon"
    
    
     height="16"
/>  Multiple documents in Pages or Microsoft Word</li>
<li>


<img
    src="https://alinpanaitiu.comimages/icons/preview_16@1x.webp#x16"
    alt="preview icon"
    
    
     height="16"
/>  Lots of PDFs open for referencing in Preview</li>
</ul>
<h3 id="available-solutions">Available solutions</h3>
<ol>
<li>
<p><strong>App Expose:</strong> Command Tab allows pressing the <code>↓ Down Arrow</code> key with the app icon selected, to expose all the windows of that app for visual selection.</p>
<ul>
<li>It’s nice and useful for when you churn windows a lot, but it’s way too slow for cases when you mostly have the same windows open.</li>
</ul>
</li>
<li>
<p><strong>Command backtick <code>`</code>:</strong> this native macOS hotkey will cycle through the windows of the current app but we’re back to square one where you have to visually analyze each window to see if you got the right one in focus.</p>
</li>
<li>
<p><strong><a href="https://alt-tab-macos.netlify.app/"  target="_blank" rel="noopener" >Alt-Tab</a>:</strong> this is a really nice open source app which replicates the Microsoft Windows way of selecting windows by thumbnails.</p>
<ul>
<li>It’s what I used for a long time, until I got too frustrated with the fact that all my seven Sublime Text windows look exactly the same and I have to also read the whole window title to find the one I want to focus.</li>
</ul>
</li>
<li>
<p><a href="https://contexts.co"  target="_blank" rel="noopener" ><strong>Contexts.co</strong></a>: a fuzzy searcher for window titles. I’ve used it in the past and it was definitely faster than the rest but it still required more key presses than I wanted</p>
<ul>
<li>I don’t really need to search the whole window title, just the project name.</li>
</ul>
</li>
<li>
<p><strong>Stage Manager:</strong> the new addition in macOS Ventura, which in its current state is just <em>discoverable Spaces</em>.</p>
<ul>
<li>That’s the feeling I got from using it: stages are just like spaces, but more visible (through the left sidebar) and easier to reach for (by clicking on them or by focusing a window in a specific stage).</li>
<li>It still doesn’t provide any keyboard control and moving specific windows in and out of the stages requires too much work with the mouse.</li>
<li>At least for Spaces I had <a href="https://github.com/koekeishiya/yabai"  target="_blank" rel="noopener" >yabai</a> to provide keyboard shortcuts for moving the current window to whatever space I wanted to.</li>
</ul>
</li>
</ol>
<h3 id="my-preferred-solution-the-right-option-key">My preferred solution: the Right Option key</h3>
<p>It’s a sunny day in Brașov, I’m on my balcony taking in the sun, testing and perfecting <a href="https://lunar.fyi/#xdr"  target="_blank" rel="noopener" >XDR Brightness</a> to make working in direct sunlight easier on my MacBook 14” while also rewriting parts of the <strong>


<img
    src="https://alinpanaitiu.comimages/icons/lunar_16@1x.webp#x16"
    alt="Lunar Icon"
    
    
     height="16"
/> <a href="https://lunar.fyi"  target="_blank" rel="noopener" >Lunar</a></strong> UI in SwiftUI.</p>


<div style="width: 100%; display: flex; justify-content: center; align-items: center; flex-direction: column;">
    <video disablepictureinpicture autoplay loop muted playsinline preload="none" style="height: 80%; max-height: 250px; margin: 10px auto; border-radius: 8px;" poster="https://alinpanaitiu.comimages/auto-xdr-sunlight-brasov.webp" src="/video/auto-xdr-sunlight-brasov.mp4">
    </video>
    <em>Testing Auto XDR</em>
</div>

<p>I’ve already written a lot of SwiftUI boilerplate in my other projects, so I’m mostly copy pasting stuff between Sublime Text windows. I also have three Sublime windows with disassembled macOS private frameworks to look for the hidden functions I need to improve the XDR Brightness curve and responsiveness.</p>
<p>Juggling with all these windows suddenly became very frustrating.</p>
<p>Why can’t I focus exactly the window I want with one hotkey just like I focus apps with rcmd?</p>
<p>I’m probably going to have the same set of windows for the next few days, I know the names of the projects I have open in them, I could use the first letter of the project name to reference a specific window.</p>
<p>The <code>Right Command</code> key is taken, but right beside it stands another rarely used key: the <code>Right Option</code> key <em>(<code>ralt</code> for short)</em></p>
<p>I want to be able to press <code>ralt-r</code> to focus the Sublime window containing the <strong>r</strong>cmd project, <code>ralt-l</code> to focus the <strong>L</strong>unar project, <code>ralt-v</code> for the <strong>V</strong>olum project, <code>ralt-p</code> to get to the <strong>P</strong>rivateFrameworks folder and so on.</p>
<p>The plan seems simple enough:</p>
<ul>
<li>get the list of windows and their title from the current app</li>
<li>extract the first letter of the project name</li>
<li>assign <code>Right Option</code> + <code>letter</code> to some <code>focusWindow</code> function</li>
<li>get back to the real work</li>
</ul>
<h2 id="oh-right--the-sandbox">Oh right … the sandbox</h2>
<p>It’s not like the above hasn’t been done before, there are plenty of window switcher and snap/resize examples on macOS, some of them are even open source:</p>
<ul>
<li><a href="https://github.com/koekeishiya/yabai"  target="_blank" rel="noopener" >yabai</a></li>
<li><a href="https://ianyh.com/amethyst/"  target="_blank" rel="noopener" >Amethyst</a></li>
<li><a href="https://github.com/rxhanson/Rectangle"  target="_blank" rel="noopener" >Rectangle</a></li>
</ul>
<p>One window snapping tools is even on the App Store: <a href="https://apps.apple.com/ro/app/magnet/id441258766?mt=12"  target="_blank" rel="noopener" >Magnet</a></p>
<p>But why are there no window switchers on the App Store?</p>
<p>Well, for app switching, Apple provides a really nice API to enumerate and activate running apps without needing any intrusive permissions: <a href="https://developer.apple.com/documentation/appkit/nsrunningapplication"  target="_blank" rel="noopener" >NSRunningApplication</a></p>
<p><em>Finding Xcode and focusing it</em></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">apps</span> <span class="p">=</span> <span class="n">NSWorkspace</span><span class="p">.</span><span class="n">shared</span><span class="p">.</span><span class="n">runningApplications</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">xcode</span> <span class="p">=</span> <span class="n">apps</span><span class="p">.</span><span class="bp">first</span> <span class="p">{</span> <span class="n">app</span> <span class="k">in</span>
</span></span><span class="line"><span class="cl">    <span class="n">app</span><span class="p">.</span><span class="n">bundleIdentifier</span> <span class="p">==</span> <span class="s">&#34;com.apple.dt.Xcode&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">xcode</span><span class="p">?.</span><span class="n">activate</span><span class="p">()</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>But there’s no such thing for enumerating the windows of those running apps. All of the apps that work with app windows, need to tap into the Accessibility API, the one that gives you full access to extract and modify the contents of everything visible and invisible.</p>














<a href="https://alinpanaitiu.comimages/yabai-accessibility-permissions.jpg">
  <figure>
    <figcaption>system dialog with yabai requesting Accessibility permissions</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/yabai-accessibility-permissions.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/yabai-accessibility-permissions.jpg"
        alt="system dialog with yabai requesting Accessibility permissions"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>And so, window enumeration becomes possible, by fetching the array of UI elements under the <code>AXWindows</code> attribute of an app.</p>
<p>But since a window is like just any other UI element, then there’s no <code>focus</code> or <code>activate</code> method, so how do these apps manage to focus a window?</p>
<p>Take a look at this nice and <em>intuitive</em> snippet extracted from <a href="https://github.com/koekeishiya/yabai/blob/master/src/window_manager.c#L815"  target="_blank" rel="noopener" >yabai</a>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">window_manager_make_key_window</span><span class="p">(</span><span class="n">ProcessSerialNumber</span> <span class="o">*</span><span class="n">window_psn</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">window_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">bytes1</span><span class="p">[</span><span class="mh">0xf8</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="p">[</span><span class="mh">0x04</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0xf8</span><span class="p">,</span> <span class="p">[</span><span class="mh">0x08</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x01</span><span class="p">,</span> <span class="p">[</span><span class="mh">0x3a</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x10</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">bytes2</span><span class="p">[</span><span class="mh">0xf8</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="p">[</span><span class="mh">0x04</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0xf8</span><span class="p">,</span> <span class="p">[</span><span class="mh">0x08</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x02</span><span class="p">,</span> <span class="p">[</span><span class="mh">0x3a</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x10</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">memcpy</span><span class="p">(</span><span class="n">bytes1</span> <span class="o">+</span> <span class="mh">0x3c</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">window_id</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">uint32_t</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">memset</span><span class="p">(</span><span class="n">bytes1</span> <span class="o">+</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0xFF</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">memcpy</span><span class="p">(</span><span class="n">bytes2</span> <span class="o">+</span> <span class="mh">0x3c</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">window_id</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">uint32_t</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">memset</span><span class="p">(</span><span class="n">bytes2</span> <span class="o">+</span> <span class="mh">0x20</span><span class="p">,</span> <span class="mh">0xFF</span><span class="p">,</span> <span class="mh">0x10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">SLPSPostEventRecordTo</span><span class="p">(</span><span class="n">window_psn</span><span class="p">,</span> <span class="n">bytes1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">SLPSPostEventRecordTo</span><span class="p">(</span><span class="n">window_psn</span><span class="p">,</span> <span class="n">bytes2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Even though I knew that <em><strong>key window</strong></em>  meant focused window in macOS terminology, it still took me a while to land on this code and start believing that this is really focusing a window.</p>
<p>In the end, what that code represents is message passing to the <strong>SkyLight</strong> private framework, the one that handles the macOS window management, Dock, Spaces and a ton of other stuff. <em>I’m guessing someone sneaked in a VM debugger or looked through the assembly code to find the right bytes to send.</em></p>
<p>Ok, enumeration and focusing is doable, what else do we need? Right, <strong>Accessibility permissions</strong>. Here comes the biggest hurdle.</p>
<h3 id="how-do-you-escape-the-macos-sandbox">How do you escape the macOS sandbox?</h3>
<p>You don’t.</p>
<p>On macOS, an app can be run:</p>
<ul>
<li>within a sandbox
<ul>
<li><em>where it has its own limited view of the file system and limited access to privileged APIs</em></li>
</ul>
</li>
<li>outside the sandbox
<ul>
<li><em>where it has access to everything that’s not guarded by SIP (System Integrity Protection)</em></li>
</ul>
</li>
</ul>
<p><strong>App Store apps can only run inside the sandbox</strong>, and within that, an app can’t ask for Accessibility permissions. The API for that just throws a silent error and does nothing.</p>
<p>But then how does Magnet do it, and a few other apps as well like Peek or PopClip for example?</p>
<p>Turns out, these apps have a special exception from Apple, mostly because they were on the App Store before the sandbox has become mandatory: <a href="https://stackoverflow.com/a/32248238"  target="_blank" rel="noopener" >objective c - How to use Accessibility with sandboxed app? - Stack Overflow</a></p>
<p>I can barely get my apps to not be rejected by the App Store reviewers, I’m not going to get an exception just so that rcmd can focus specific windows. So now what?</p>
<h2 id="workarounds">Workarounds</h2>
<p>I thought, if there was an app running outside the sandbox and listening for rcmd’s <code>listWindows</code> and <code>focusWindow</code> commands, I might be able to get this working.</p>
<p>I remembered <strong>


<img
    src="https://alinpanaitiu.comimages/icons/hammerspoon_16@1x.webp#x16"
    alt="hammerspoon icon"
    
    
     height="16"
/> <a href="https://www.hammerspoon.org"  target="_blank" rel="noopener" >Hammerspoon</a></strong> having <a href="https://www.hammerspoon.org/docs/hs.window.html"  target="_blank" rel="noopener" >a really complete window management support</a> and it also being scriptable with Lua made it the perfect choice.</p>
<p>HTTP would probably be overkill for this, I knew Hammerspoon had an inter-process communication (IPC) API built-in so I tried to use that.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="k">static</span> <span class="n">NSString</span> <span class="o">*</span><span class="n">portName</span> <span class="o">=</span> <span class="s">@&#34;Hammerspoon&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">CFMessagePortRef</span> <span class="n">messagePort</span> <span class="o">=</span> <span class="n">CFMessagePortCreateRemote</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="p">(</span><span class="k">__bridge</span> <span class="n">CFStringRef</span><span class="p">)</span><span class="n">portName</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// messagePort is NULL here
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Well nope, the sandbox doesn’t allow that.</p>
<p>What about the <code>hs</code> CLI that Hammerspoon provides, I knew that you could send arbitrary IPC messages using that, right?</p>
<p>Nope again, any process run by a sandboxed app will inherit that sandbox limitations.</p>
<p>Ok fine, HTTP it is! Thankfully Hammerspoon provides an HTTP server and I just need to register a callback and make it listen on a port. Since we’ve already reached this madness, let’s go straight to websockets.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nf">rcmdCallbackWS</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">params</span> <span class="o">=</span> <span class="n">hs.json</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">response</span> <span class="o">=</span> <span class="s2">&#34;{}&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">params.cmd</span> <span class="o">==</span> <span class="s2">&#34;listWindows&#34;</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">response</span> <span class="o">=</span> <span class="n">hs.json</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span><span class="n">hs.window</span><span class="p">:</span><span class="n">allWindows</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">    <span class="kr">elseif</span> <span class="n">params.cmd</span> <span class="o">==</span> <span class="s2">&#34;focusWindow&#34;</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">hs.window</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">params.window</span><span class="p">):</span><span class="n">focus</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">response</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">server</span> <span class="o">=</span> <span class="n">hs.httpserver</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="kc">false</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">server</span><span class="p">:</span><span class="n">setName</span><span class="p">(</span><span class="s2">&#34;rcmd-hammerspoon&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">server</span><span class="p">:</span><span class="n">setInterface</span><span class="p">(</span><span class="s2">&#34;localhost&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">server</span><span class="p">:</span><span class="n">setPort</span><span class="p">(</span><span class="mi">3094</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">server</span><span class="p">:</span><span class="n">websocket</span><span class="p">(</span><span class="s2">&#34;/ws&#34;</span><span class="p">,</span> <span class="n">rcmdCallbackWS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">server</span><span class="p">:</span><span class="n">start</span><span class="p">()</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Alright, this seems to work. I can connect to the Hammerspoon websocket, get all windows, and focus windows by their IDs.</p>
<p>Now how do I explain to rcmd users that in order to focus windows, they need to:</p>
<ul>
<li>download a zip file from GitHub releases</li>
<li>install another app with no affiliation to rcmd</li>
<li>give that app Accessibility permissions</li>
<li>install a Lua script in the <code>~/.hammerspoon</code> directory</li>
<li>ensure Hammerspoon is kept running all the time</li>
</ul>
<h2 id="automating-the-workarounds">Automating the workarounds</h2>
<p>The App Store guidelines explicitly forbid an app from installing another app or binary to enhance its capabilities.</p>
<blockquote>
<p>2.4.5 Apps distributed via the Mac App Store have some additional requirements to keep in mind:</p>
</blockquote>
<blockquote>
<p>(iv) They may not download or install standalone apps, kexts, additional code, or resources to add functionality or significantly change the app from what we see during the review process.</p>
</blockquote>
<p>So I can’t install Hammerspoon automatically <em>(it would be a bad idea anyway, this is malware behavior)</em>, but I can try to automate most of the stuff and present it as a 1-button install action.</p>
<p>So I wrote a function to download <code>Hammerspoon.zip</code>, unzip it in a temporary folder, move it to <code>/Applications</code>, write <code>init.lua</code> and <code>rcmd.lua</code> inside the <code>~/.hammerspoon</code> directory, launch Hammerspoon and wait for the websocket to be available.</p>
<p>The user only has to click an <strong>Install window switcher</strong> button, no big deal.</p>
<h3 id="quarantine-says-not-so-fast">Quarantine says “not so fast”</h3>
<p>You see, when a sandboxed app downloads a file, the system automatically adds the <code>com.apple.quarantine</code> extended attribute to the file.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-awk" data-lang="awk"><span class="line"><span class="cl"><span class="o">&gt;</span> <span class="nx">xattr</span> <span class="o">-</span><span class="nx">l</span> <span class="o">~</span><span class="err">/Downloads/Hammerspoon.zip</span>
</span></span><span class="line"><span class="cl"><span class="nx">com</span><span class="p">.</span><span class="nx">apple</span><span class="p">.</span><span class="nx">quarantine</span><span class="err">:</span> <span class="mi">0083</span><span class="p">;</span><span class="mi">62</span><span class="nx">ea4f5c</span><span class="p">;</span><span class="nx">Safari</span><span class="p">;</span><span class="mi">3</span><span class="nx">A6D521B</span><span class="o">-</span><span class="mi">5</span><span class="nx">E0D</span><span class="o">-</span><span class="mi">4202</span><span class="o">-</span><span class="mi">80</span><span class="nx">C4</span><span class="o">-</span><span class="nx">A5EB567DC246</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>This means that macOS GateKeeper will prevent you from launching any downloaded app or running any binary directly from code.</p>
<p>Even if the user tries to launch the downloaded app manually afterwards, it will still fail with the <em>App can&rsquo;t be opened</em> error.</p>














<a href="https://alinpanaitiu.comimages/hammerspoon-quarantine.jpg">
  <figure>
    <figcaption>system dialog with hammerspoon not being allowed to launch because of the quarantine attribute</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hammerspoon-quarantine.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/hammerspoon-quarantine.jpg"
        alt="system dialog with hammerspoon not being allowed to launch because of the quarantine attribute"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>No amount of <code>xattr -cr Hammerspoon.app</code> will fix this if run from the sandbox.</p>
<p>Great. Scrap the download and install part, split the button into two buttons:</p>
<ol>
<li><strong>Install Hammerspoon</strong> which only shows text instructions on how to download and install the app manually</li>
<li><strong>Install custom script</strong> which writes the Lua script files to disk</li>
</ol>














<a href="https://alinpanaitiu.comimages/rcmd-install-hs-buttons.png">
  <figure>
    <figcaption>rcmd menu showing the two install buttons</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/rcmd-install-hs-buttons.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/rcmd-install-hs-buttons.png"
        alt="rcmd menu showing the two install buttons"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>I’ve streamlined this process as much as the sandbox allows me, and after giving the app to some beta testers, every single one of them found it so confusing that they said they would not use it.</p>
<p>And who can blame them, I myself find it too convoluted whenever I test it.</p>
<h2 id="so-is-this-on-the-app-store">So is this on the App Store?</h2>
<p>Yes, surprisingly. It passed App Review without a single rejection.</p>
<p>I hid the feature behind a <strong><code>Try experimental window switching</code></strong> red button to deter support emails on the subject, but it’s there for anyone to try and use.</p>














<a href="https://alinpanaitiu.comimages/try-experimental-window-switching-button.png">
  <figure>
    <figcaption>rcmd menu showing the try experimental window switching button </figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/try-experimental-window-switching-button.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/try-experimental-window-switching-button.png"
        alt="rcmd menu showing the try experimental window switching button "
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>After the initial setup, it actually works pretty reliably, and the websocket connection to Hammerspoon is so fast that I don’t ever notice this happens over the network. It feels like a native window switcher to me.</p>
<p>But I wasn&rsquo;t able to create a seamless experience like I did for app switching.</p>
<p>Oh well, at least I solved my own problem and can get back to what I was doing.</p>
<p>One month later.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>Trying to get past the 500 nits limit of the MacBook Pro (and failing)</title>
      <link>https://alinpanaitiu.com/blog/over-500nits-failed/</link>
      <pubDate>Fri, 04 Feb 2022 19:26:36 +0200</pubDate>
      <author>alin@panaitiu.com (Alin Panaitiu)</author>
      <guid>https://alinpanaitiu.com/blog/over-500nits-failed/</guid>
      <description>
        <![CDATA[<p><strong>Update: I finally found a way to go over the limit in <a href="https://lunar.fyi/changelog#5.5.1"  target="_blank" rel="noopener" >Lunar v5.5.1</a></strong></p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube-nocookie.com/embed/jC3yyFKPcCI" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="Lunar XDR Brightness demo on MacBook Pro 2021"></iframe>
</div>

<hr>
<p>Exactly 3 months and a day after placing an order through a Romanian Apple reseller, I finally got my 14-inch M1 Max.</p>
<p><em>Well, actually.. I first got the wrong configuration (base model instead of CTO), had to return it to them after wasting a day on migrating my data to it, they sent my money back by mistake, had to pay them again, and after many calls and emails later the correct laptop arrived.</em></p>














<a href="https://alinpanaitiu.comimages/m1max.jpg">
  <figure>
    <figcaption>M1 Max MacBook Pro box</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/m1max.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/m1max.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/m1max.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/m1max.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/m1max.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/m1max.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/m1max.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/m1max.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/m1max.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/m1max.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/m1max.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/m1max.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/m1max.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/m1max.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/m1max.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/m1max.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/m1max.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/m1max.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/m1max.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/m1max.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/m1max.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/m1max.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/m1max.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/m1max.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/m1max.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/m1max.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/m1max.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/m1max.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/m1max.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/m1max.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/m1max.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/m1max.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/m1max.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/m1max.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/m1max.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/m1max.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/m1max.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/m1max.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/m1max.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/m1max.jpg"
        alt="M1 Max MacBook Pro box"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>As soon as these devices were in the hands of users, requests started coming in for <strong><a href="https://lunar.fyi"  target="_blank" rel="noopener" >Lunar</a></strong> to provide an option to <a href="https://github.com/alin23/Lunar/issues/417"  target="_blank" rel="noopener" >get past the 500 nits limit for everyday usage</a></p>
<p>Over the last week I tried my best to figure out how to do this, but it&rsquo;s either impossible to raise the nits limit from userspace, or I just don&rsquo;t have the necessary expertise.</p>
<p>I&rsquo;ll share some details that I found while reverse engineering my way through the macOS part that handles brightness.</p>
<h2 id="testing-the-system">Testing the system</h2>
<h3 id="playing-a-hdr-video">Playing a HDR video</h3>
<p>I first started by playing this HDR test video <em>(open it in latest Chrome or Safari for best results)</em>: <a href="https://files.alinpanaitiu.com/hdr-test-pattern.webm"  target="_blank" rel="noopener" >hdr-test-pattern.webm</a></p>
<p>Which resulted in a blinding white at 1600 nits:<br>














<a href="https://alinpanaitiu.comimages/hdr-result.jpg">
  <figure>
    <figcaption>HDR white being whiter than the webpage white</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hdr-result.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hdr-result.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hdr-result.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hdr-result.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hdr-result.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hdr-result.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hdr-result.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hdr-result.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hdr-result.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hdr-result.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hdr-result.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hdr-result.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hdr-result.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hdr-result.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hdr-result.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hdr-result.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hdr-result.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hdr-result.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hdr-result.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hdr-result.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hdr-result.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hdr-result.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hdr-result.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hdr-result.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hdr-result.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hdr-result.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hdr-result.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hdr-result.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hdr-result.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hdr-result.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hdr-result.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hdr-result.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hdr-result.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hdr-result.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hdr-result.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hdr-result.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hdr-result.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hdr-result.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hdr-result.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/hdr-result.jpg"
        alt="HDR white being whiter than the webpage white"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>
</p>
<p>This generated the following logs in Console.app:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">888.889</span>
</span></span><span class="line"><span class="cl"><span class="n">corebrightnessd</span> <span class="n">SDR</span> <span class="o">-</span> <span class="n">perceptual</span> <span class="n">ramp</span> <span class="n">clocked</span><span class="p">:</span> <span class="mf">227.095169</span> <span class="p">-&gt;</span> <span class="mf">252.268112</span> <span class="o">-</span> <span class="mf">49.169426</span><span class="o">%</span> <span class="p">(</span><span class="mf">239.142059</span> <span class="n">Nits</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">211.603</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mf">4.20075</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">4.3396</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">13.6333</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mi">1600</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h3 id="sdr-cap-in-normal-lighting">SDR cap in normal lighting</h3>
<p>After setting the display brightness to max, I could see in the logs that <strong>SDR</strong> (Standard Dynamic Range) was being capped at <strong>400 nits</strong>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mi">1600</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">display</span> <span class="n">headroom</span> <span class="n">hint</span> <span class="n">to</span> <span class="mf">7.56866</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">216.548</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mf">7.38865</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">4.24854</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">13.3472</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mi">1600</span>
</span></span><span class="line"><span class="cl"><span class="n">corebrightnessd</span> <span class="n">PCC</span><span class="p">:</span> <span class="n">Set</span> <span class="n">PCC</span><span class="p">:</span> <span class="n">Factor</span><span class="p">:=</span><span class="mf">1.0496</span> <span class="n">CabalFactor</span><span class="p">:=</span><span class="mf">0.0033</span> <span class="n">time</span><span class="p">=</span><span class="mf">2.000000</span> <span class="n">Lux</span><span class="p">:=</span><span class="mf">13.6080</span> <span class="n">Nits</span><span class="p">:=</span><span class="mf">229.1757</span> <span class="n">result</span><span class="p">=</span><span class="mi">1</span> <span class="n">error</span><span class="p">=(</span><span class="n">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">301.188</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mf">5.3123</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">4.24854</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">13.3472</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mi">1600</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">1602.03</span>
</span></span><span class="line"><span class="cl"><span class="n">corebrightnessd</span> <span class="n">levelPercentage</span> <span class="mf">0.334298</span><span class="p">,</span> <span class="n">level</span> <span class="p">=</span> <span class="mf">4.967383</span> <span class="p">(</span><span class="n">nits</span><span class="o">/</span><span class="n">pwm</span><span class="p">),</span> <span class="n">lux</span> <span class="p">=</span> <span class="mf">15.000000</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">301.571</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">4.79275</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">15.0569</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">display</span> <span class="n">headroom</span> <span class="n">hint</span> <span class="n">to</span> <span class="mf">5.27556</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">321.478</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mf">4.97701</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">4.79275</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">15.0569</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mi">1600</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">340.675</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mf">4.69655</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">4.79275</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">15.0569</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mi">1600</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">377.322</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mf">4.24041</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">4.79275</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">15.0569</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mi">1600</span>
</span></span><span class="line"><span class="cl"><span class="n">corebrightnessd</span> <span class="n">PCC</span><span class="p">:</span> <span class="n">Set</span> <span class="n">PCC</span><span class="p">:</span> <span class="n">Factor</span><span class="p">:=</span><span class="mf">1.0340</span> <span class="n">CabalFactor</span><span class="p">:=</span><span class="mf">0.0023</span> <span class="n">time</span><span class="p">=</span><span class="mf">2.000000</span> <span class="n">Lux</span><span class="p">:=</span><span class="mf">15.0569</span> <span class="n">Nits</span><span class="p">:=</span><span class="mf">377.3223</span> <span class="n">result</span><span class="p">=</span><span class="mi">1</span> <span class="n">error</span><span class="p">=(</span><span class="n">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mi">1600</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">display</span> <span class="n">headroom</span> <span class="n">hint</span> <span class="n">to</span> <span class="mi">4</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mi">400</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">4.96577</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">15.6004</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span>
</span></span></code></pre></td></tr></table>
</div>
</div>













<a href="https://alinpanaitiu.comimages/hdr-console.jpg">
  <figure>
    <figcaption>HDR white and console logs side by side</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hdr-console.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hdr-console.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hdr-console.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hdr-console.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hdr-console.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hdr-console.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hdr-console.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hdr-console.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hdr-console.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hdr-console.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hdr-console.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hdr-console.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hdr-console.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hdr-console.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hdr-console.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hdr-console.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hdr-console.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hdr-console.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hdr-console.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hdr-console.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hdr-console.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hdr-console.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hdr-console.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hdr-console.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hdr-console.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hdr-console.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/hdr-console.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/hdr-console.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/hdr-console.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/hdr-console.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/hdr-console.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/hdr-console.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/hdr-console.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/hdr-console.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/hdr-console.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/hdr-console.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/hdr-console.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/hdr-console.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/hdr-console.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/hdr-console.jpg"
        alt="HDR white and console logs side by side"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<hr>
<h3 id="sdr-cap-in-direct-sunlight">SDR cap in direct sunlight</h3>
<p>Shining a flashlight directly into the Ambient Light Sensor allowed <strong>SDR</strong> to jump up to <strong>500 nits</strong>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mi">400</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">322.204</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">1012.24</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">400.484</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">322.204</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">1012.24</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mf">400.484</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">400.484</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">401.15</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">322.204</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">1012.24</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mf">401.15</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">401.15</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">401.223</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">322.204</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">1012.24</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mf">401.224</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">401.223</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">401.223</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">370.814</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">1164.95</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">401.552</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">370.814</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">1164.95</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mf">401.552</span>
</span></span><span class="line"><span class="cl"><span class="n">corebrightnessd</span> <span class="n">PCC</span><span class="p">:</span> <span class="n">Set</span> <span class="n">PCC</span><span class="p">:</span> <span class="n">Factor</span><span class="p">:=</span><span class="mf">1.7464</span> <span class="n">CabalFactor</span><span class="p">:=</span><span class="mf">0.0498</span> <span class="n">time</span><span class="p">=</span><span class="mf">2.000000</span> <span class="n">Lux</span><span class="p">:=</span><span class="mf">1164.9467</span> <span class="n">Nits</span><span class="p">:=</span><span class="mf">401.5517</span> <span class="n">result</span><span class="p">=</span><span class="mi">1</span> <span class="n">error</span><span class="p">=(</span><span class="n">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">401.552</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">402.219</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">370.814</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">1164.95</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mf">402.219</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">402.219</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">402.885</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">370.814</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">1164.95</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mf">402.885</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">402.885</span>
</span></span><span class="line"><span class="cl"><span class="p">...</span> <span class="n">lots</span> <span class="n">of</span> <span class="n">similar</span> <span class="n">logs</span> <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">495.458</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">496.125</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">810.176</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">2545.24</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mf">496.125</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">496.125</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">496.791</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">810.176</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">2545.24</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mf">496.792</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">496.791</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">497.458</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">810.176</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">2545.24</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mf">497.458</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">497.458</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">498.125</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">810.176</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">2545.24</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mf">498.125</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">498.125</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">498.791</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">810.176</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">2545.24</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mf">498.792</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">498.791</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mf">499.458</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">810.176</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">2545.24</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mf">499.458</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mf">499.458</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mi">500</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">810.176</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">2545.24</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mi">500</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">setting</span> <span class="n">nits</span> <span class="n">to</span> <span class="mi">500</span>
</span></span><span class="line"><span class="cl"><span class="n">WindowServer</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mi">500</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">987.858</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="mf">3103.45</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="dissecting-the-system">Dissecting the system</h2>
<p>Since Big Sur, macOS transitioned from having the frameworks on the disk as separate binaries, to having a single file containing all the system libraries, called a <code>dyld_shared_cache</code>.</p>
<blockquote>
<ul>
<li>New in macOS Big Sur 11.0.1, the system ships with a built-in dynamic linker cache of all system-provided libraries. As part of this change, copies of dynamic libraries are no longer present on the filesystem. Code that attempts to check for dynamic library presence by looking for a file at a path or enumerating a directory will fail. Instead, check for library presence by attempting to dlopen() the path, which will correctly check for the library in the cache. (62986286)</li>
</ul>
</blockquote>
<p>Searching for keywords from the above logs surfaced only the dyld cache as expected.</p>














<a href="https://alinpanaitiu.comimages/dyld-cache-rg-result.png">
  <figure>
    <figcaption>searching for nits in system</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/dyld-cache-rg-result.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/dyld-cache-rg-result.png"
        alt="searching for nits in system"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>I used <a href="https://github.com/keith/dyld-shared-cache-extractor"  target="_blank" rel="noopener" >dyld-shared-cache-extractor</a> to drop the separate binaries on disk, then did another search there.</p>
<p>This surfaced up <code>QuartzCore</code> as the single place where that string could be found.</p>














<a href="https://alinpanaitiu.comimages/dyld-cache-extracted-rg-result.png">
  <figure>
    <figcaption>searching for nits in extracted dyld cache</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/dyld-cache-extracted-rg-result.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/dyld-cache-extracted-rg-result.png"
        alt="searching for nits in extracted dyld cache"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<hr>
<h3 id="trying-to-abuse-quartzcore">Trying to abuse QuartzCore</h3>
<p>After looking through the QuartzCore binary with Ghidra and finding some iOS headers for it on <a href="https://developer.limneos.net/index.php?ios=15.2.1&amp;framework=QuartzCore.framework&amp;header=CAWindowServer.h"  target="_blank" rel="noopener" >limneos.net</a>, I created a sample Swift project to try to use some of the exported functions from it: <a href="https://github.com/alin23/monitorpanel/blob/nits-limit/Sources/monitorpanel/main.swift"  target="_blank" rel="noopener" >monitorpanel - main.swift</a></p>
<p>Based on some open-sourced iOS jailbreak tweaks, I noticed that developers used the <code>CAWindowServer</code> class to interface with the display and HID components directly. The class was available here so I tried to do the same on macOS.</p>
<p>Unfortunately, <code>CAWindowServer.serverIfRunning</code> always returns <code>nil</code> and while <code>CAWindowServer.server(withOptions: nil)</code> returns a seemingly valid server, all external displays are forcefully disconnected when that server is created.</p>
<p>Using the below code, I succeeded in producing the <code>commitBrightness</code> log line in Console, but nothing really changed.</p>
<p><strong>code from <a href="https://github.com/alin23/monitorpanel/blob/nits-limit/Sources/monitorpanel/main.swift"  target="_blank" rel="noopener" >main.swift</a></strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">setToMax</span><span class="p">(</span><span class="kc">_</span> <span class="n">d</span><span class="p">:</span> <span class="n">CAWindowServerDisplay</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="p">.</span><span class="n">setBrightnessLimit</span><span class="p">(</span><span class="mi">1600</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="p">.</span><span class="n">setHeadroom</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="p">.</span><span class="n">maximumBrightness</span> <span class="p">=</span> <span class="mf">1000.0</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="p">.</span><span class="n">setSDRBrightness</span><span class="p">(</span><span class="mi">600</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="p">.</span><span class="n">maximumHDRLuminance</span> <span class="p">=</span> <span class="mi">1600</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="p">.</span><span class="n">maximumReferenceLuminance</span> <span class="p">=</span> <span class="mi">1600</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="p">.</span><span class="n">maximumSDRLuminance</span> <span class="p">=</span> <span class="mi">1000</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="p">.</span><span class="n">contrast</span> <span class="p">=</span> <span class="mf">1.1</span>
</span></span><span class="line"><span class="cl">    <span class="n">d</span><span class="p">.</span><span class="n">commitBrightness</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// d.update() // segfault</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">ws</span><span class="p">:</span> <span class="n">CAWindowServer</span><span class="p">?</span> <span class="p">=</span> <span class="p">(</span><span class="n">CAWindowServer</span><span class="p">.</span><span class="n">server</span><span class="p">(</span><span class="n">withOptions</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span> <span class="k">as</span><span class="p">?</span> <span class="n">CAWindowServer</span><span class="p">)</span> <span class="c1">// disconnects external displays</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="kd">let</span> <span class="nv">ws</span> <span class="p">=</span> <span class="n">ws</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="kd">let</span> <span class="nv">displays</span> <span class="p">=</span> <span class="n">ws</span><span class="p">.</span><span class="n">displays</span> <span class="k">as</span><span class="p">?</span> <span class="p">[</span><span class="n">CAWindowServerDisplay</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">   <span class="kd">let</span> <span class="nv">d</span> <span class="p">=</span> <span class="n">displays</span><span class="p">.</span><span class="bp">first</span><span class="p">(</span><span class="k">where</span><span class="p">:</span> <span class="p">{</span> <span class="nv">$0</span><span class="p">.</span><span class="n">deviceName</span> <span class="p">==</span> <span class="s">&#34;primary&#34;</span> <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">setToMax</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong><code>commitBrightness</code> log line</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">monitorpanel</span>    <span class="n">Display</span> <span class="mi">1</span> <span class="n">commitBrightness</span> <span class="n">sdr</span><span class="p">:</span> <span class="mi">600</span><span class="p">,</span> <span class="n">headroom</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ambient</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">filtered</span> <span class="n">ambient</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">limit</span><span class="p">:</span> <span class="mi">1600</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="corebrightness">CoreBrightness</h3>
<p>While looking through Ghidra, I noticed that <code>QuartzCore</code> finally calls into <code>CoreBrightness</code> functions to increase the nits limit, so I took a look at the exported symbols on that binary.</p>
<p>Unfortunately, all the possibly useful symbols are not exported and trying to link against them would result in the <code>undefined symbols</code> error.</p>
<p>Adding the private symbols in the <a href="https://github.com/alin23/monitorpanel/blob/nits-limit/Sources/CoreBrightness.framework/CoreBrightness.tbd"  target="_blank" rel="noopener" >CoreBrightness.tbd</a> file doesn&rsquo;t help in this case.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">  1
</span><span class="lnt">  2
</span><span class="lnt">  3
</span><span class="lnt">  4
</span><span class="lnt">  5
</span><span class="lnt">  6
</span><span class="lnt">  7
</span><span class="lnt">  8
</span><span class="lnt">  9
</span><span class="lnt"> 10
</span><span class="lnt"> 11
</span><span class="lnt"> 12
</span><span class="lnt"> 13
</span><span class="lnt"> 14
</span><span class="lnt"> 15
</span><span class="lnt"> 16
</span><span class="lnt"> 17
</span><span class="lnt"> 18
</span><span class="lnt"> 19
</span><span class="lnt"> 20
</span><span class="lnt"> 21
</span><span class="lnt"> 22
</span><span class="lnt"> 23
</span><span class="lnt"> 24
</span><span class="lnt"> 25
</span><span class="lnt"> 26
</span><span class="lnt"> 27
</span><span class="lnt"> 28
</span><span class="lnt"> 29
</span><span class="lnt"> 30
</span><span class="lnt"> 31
</span><span class="lnt"> 32
</span><span class="lnt"> 33
</span><span class="lnt"> 34
</span><span class="lnt"> 35
</span><span class="lnt"> 36
</span><span class="lnt"> 37
</span><span class="lnt"> 38
</span><span class="lnt"> 39
</span><span class="lnt"> 40
</span><span class="lnt"> 41
</span><span class="lnt"> 42
</span><span class="lnt"> 43
</span><span class="lnt"> 44
</span><span class="lnt"> 45
</span><span class="lnt"> 46
</span><span class="lnt"> 47
</span><span class="lnt"> 48
</span><span class="lnt"> 49
</span><span class="lnt"> 50
</span><span class="lnt"> 51
</span><span class="lnt"> 52
</span><span class="lnt"> 53
</span><span class="lnt"> 54
</span><span class="lnt"> 55
</span><span class="lnt"> 56
</span><span class="lnt"> 57
</span><span class="lnt"> 58
</span><span class="lnt"> 59
</span><span class="lnt"> 60
</span><span class="lnt"> 61
</span><span class="lnt"> 62
</span><span class="lnt"> 63
</span><span class="lnt"> 64
</span><span class="lnt"> 65
</span><span class="lnt"> 66
</span><span class="lnt"> 67
</span><span class="lnt"> 68
</span><span class="lnt"> 69
</span><span class="lnt"> 70
</span><span class="lnt"> 71
</span><span class="lnt"> 72
</span><span class="lnt"> 73
</span><span class="lnt"> 74
</span><span class="lnt"> 75
</span><span class="lnt"> 76
</span><span class="lnt"> 77
</span><span class="lnt"> 78
</span><span class="lnt"> 79
</span><span class="lnt"> 80
</span><span class="lnt"> 81
</span><span class="lnt"> 82
</span><span class="lnt"> 83
</span><span class="lnt"> 84
</span><span class="lnt"> 85
</span><span class="lnt"> 86
</span><span class="lnt"> 87
</span><span class="lnt"> 88
</span><span class="lnt"> 89
</span><span class="lnt"> 90
</span><span class="lnt"> 91
</span><span class="lnt"> 92
</span><span class="lnt"> 93
</span><span class="lnt"> 94
</span><span class="lnt"> 95
</span><span class="lnt"> 96
</span><span class="lnt"> 97
</span><span class="lnt"> 98
</span><span class="lnt"> 99
</span><span class="lnt">100
</span><span class="lnt">101
</span><span class="lnt">102
</span><span class="lnt">103
</span><span class="lnt">104
</span><span class="lnt">105
</span><span class="lnt">106
</span><span class="lnt">107
</span><span class="lnt">108
</span><span class="lnt">109
</span><span class="lnt">110
</span><span class="lnt">111
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="c1">// Uninteresting Exported Symbols
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="n">_OBJC_CLASS_</span><span class="err">$</span><span class="n">_BrightnessSystem</span>
</span></span><span class="line"><span class="cl"><span class="n">_OBJC_CLASS_</span><span class="err">$</span><span class="n">_BrightnessSystemClient</span>
</span></span><span class="line"><span class="cl"><span class="n">_OBJC_CLASS_</span><span class="err">$</span><span class="n">_BrightnessSystemClientInternal</span>
</span></span><span class="line"><span class="cl"><span class="n">_OBJC_CLASS_</span><span class="err">$</span><span class="n">_CBAdaptationClient</span>
</span></span><span class="line"><span class="cl"><span class="n">_OBJC_CLASS_</span><span class="err">$</span><span class="n">_CBBlueLightClient</span>
</span></span><span class="line"><span class="cl"><span class="n">_OBJC_CLASS_</span><span class="err">$</span><span class="n">_CBClient</span>
</span></span><span class="line"><span class="cl"><span class="n">_OBJC_CLASS_</span><span class="err">$</span><span class="n">_CBKeyboardPreferencesManager</span>
</span></span><span class="line"><span class="cl"><span class="n">_OBJC_CLASS_</span><span class="err">$</span><span class="n">_CBTrueToneClient</span>
</span></span><span class="line"><span class="cl"><span class="n">_OBJC_CLASS_</span><span class="err">$</span><span class="n">_DisplayServicesClient</span>
</span></span><span class="line"><span class="cl"><span class="n">_OBJC_CLASS_</span><span class="err">$</span><span class="n">_KeyboardBrightnessClient</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Interesting Not Exported Symbols
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBBrightnessProxySKL</span> <span class="n">brightnessNotificationRequestEDR</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBBrightnessProxySKL</span> <span class="n">brightnessRequestEDRHeadroom</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBBrightnessProxySKL</span> <span class="n">brightnessRequestRampDuration</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBBrightnessProxySKL</span> <span class="nl">commitBrightness</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBBrightnessProxySKL</span> <span class="nl">initWithSLSBrightnessControl</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBBrightnessProxySKL</span> <span class="nl">setAmbient</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBBrightnessProxySKL</span> <span class="nl">setBrightnessLimit</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBBrightnessProxySKL</span> <span class="nl">setHeadroom</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBBrightnessProxySKL</span> <span class="nl">setNotificationQueue</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBBrightnessProxySKL</span> <span class="nl">setPotentialHeadroom</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBBrightnessProxySKL</span> <span class="nl">setSDRBrightness</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBBrightnessProxySKL</span> <span class="nl">setWhitePoint</span><span class="p">:</span><span class="nl">rampDuration</span><span class="p">:</span><span class="nl">error</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBBrightnessProxySKL</span> <span class="n">unregisterNotificationBlocks</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">configureEDRSecPerStop</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">configurePCCDefaults</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">getBrightnessLimit</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">getDynamicSliderAdjustedNits</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">getDynamicSliderAdjustedSDRNits</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">getLinearBrightnessForNits</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">getLinearBrightness</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">getMaxNitsAdjusted</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">getMaxNitsEDR</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">getMaxPanelNits</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">getNitsForLinearBrightness</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">getNitsForUserBrightness</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">getPerceptualBrightness</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">getSDRBrightnessCurrent</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">getSDRBrightnessTarget</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">getSDRNitsCapped</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">getUserBrightnessForNits</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">getUserBrightnessSloperExtended</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">getUserBrightness</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">handleBrightnessCapOverride</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">initialiseEDR</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">initialiseSDR</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">luminanceToPerceptual</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">panelMaxNitsOverride</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">perceptualToLuminance</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">rampDynamicSlider</span><span class="p">:</span><span class="nl">withLength</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">rampEDRHedroom</span><span class="p">:</span><span class="nl">withLength</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">rampFactor</span><span class="p">:</span><span class="nl">withLength</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">rampManagerUpdateHandling</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">rampNitsCap</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">rampSDRBrightness</span><span class="p">:</span><span class="nl">withLength</span><span class="p">:</span><span class="nl">properties</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">requestEDRHeadroomImmediate</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">requestEDRHeadroomTransition</span><span class="p">:</span><span class="nl">withLength</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">requestEDRHeadroomTransitionStop</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">requestFactorImmediate</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">requestFactorTransition</span><span class="p">:</span><span class="nl">withLength</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">requestFactorTransitionStop</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">requestSDRBrightnessTransition</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">requestSDRBrightnessTransition</span><span class="p">:</span><span class="nl">withLength</span><span class="p">:</span><span class="nl">properties</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">requestSDRBrightnessTransitionStop</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">supportsDynamicSlider</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">supportsEDR</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">supportsSDRBrightness</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">updateAmbient</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">updateAutoBrightnessState</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">updateBrightnessState</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">updateContrastEnhancerState</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">updateDynamicSliderAmbient</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">updateDynamicSliderAutoBrightness</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">updateDynamicSliderChargerState</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">updateDynamicSliderScaler</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="n">updateEDRAmbient</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">updateSDRBrightness</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBDisplayModuleSKL</span> <span class="nl">updateSDRNits</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="n">appliedCompensation</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="n">availableHeadroom</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="n">brightnessCap</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="nl">cappedHeadroomFromUncapped</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="n">copyStatusInfo</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="n">description</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="nl">initWithRampPolicy</span><span class="p">:</span><span class="nl">potentialHeadroom</span><span class="p">:</span><span class="nl">andReferenceHeadroom</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="n">maxHeadroom</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="n">panelMax</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="n">referenceHeadroom</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="n">sanityCheck</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="n">sdrBrightness</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="n">secondsPerStop</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="nl">setAppliedCompensation</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="nl">setBrightnessCap</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="nl">setPanelMax</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="nl">setSdrBrightness</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="nl">setSecondsPerStop</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="nl">shouldUpdateEDRForRequestedHeadroom</span><span class="p">:</span><span class="nl">targetHeadroom</span><span class="p">:</span><span class="nl">rampTime</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBEDR</span> <span class="nl">stopsFromHeadroomRatio</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBNVRAM</span> <span class="n">backlightNitsDefault</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBNVRAM</span> <span class="n">backlightNitsMax</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBNVRAM</span> <span class="n">backlightNitsMin</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBNVRAM</span> <span class="n">dealloc</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBNVRAM</span> <span class="n">init</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBNVRAM</span> <span class="n">readBacklightNits</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBNVRAM</span> <span class="nl">setBacklightNitsMax</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="p">[</span><span class="n">CBNVRAM</span> <span class="nl">writeBacklightNits</span><span class="p">:]</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="skylight">SkyLight</h3>
<p>I knew from previous work on window management that the <code>SkyLight</code> framework is closely related to the WindowServer so I took a look at that too.</p>
<p>SkyLight exports a lot of symbols, and fortunately I had a good example on how to use them inside <a href="https://github.com/koekeishiya/yabai/blob/master/src/misc/extern.h"  target="_blank" rel="noopener" >yabai</a>, a macOS window manager similar to i3 and bspwm.</p>
<p>But again, nothing useful is exported.</p>














<a href="https://alinpanaitiu.comimages/skylight-symbols.png">
  <figure>
    <figcaption>Searching for nits in SkyLight</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/skylight-symbols.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/skylight-symbols.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/skylight-symbols.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/skylight-symbols.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/skylight-symbols.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/skylight-symbols.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/skylight-symbols.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/skylight-symbols.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/skylight-symbols.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/skylight-symbols.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/skylight-symbols.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/skylight-symbols.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/skylight-symbols.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/skylight-symbols.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/skylight-symbols.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/skylight-symbols.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/skylight-symbols.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/skylight-symbols.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/skylight-symbols.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/skylight-symbols.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/skylight-symbols.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/skylight-symbols.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/skylight-symbols.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/skylight-symbols.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/skylight-symbols.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/skylight-symbols.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/skylight-symbols.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/skylight-symbols.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/skylight-symbols.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/skylight-symbols.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/skylight-symbols.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/skylight-symbols.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/skylight-symbols.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/skylight-symbols.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/skylight-symbols.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/skylight-symbols.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/skylight-symbols.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/skylight-symbols.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/skylight-symbols.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/skylight-symbols.png"
        alt="Searching for nits in SkyLight"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p><del>The function <code>kSLSBrightnessRequestEDRHeadroom</code> seemed promising but I always got a <code>SIGBUS</code> when trying to call it. I can&rsquo;t find its implementation so I don&rsquo;t know what parameters I should pass. I just guessed the first one could be a display ID.</del></p>
<p>As one Hacker News user pointed out, <code>kSLSBrightnessRequestEDRHeadroom</code> is actually a constant. And of course it is! It has the usual <code>k</code> prefix.. how did I miss that?</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-objc" data-lang="objc"><span class="line"><span class="cl"><span class="p">@</span><span class="n">import</span> <span class="n">Darwin</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="n">import</span> <span class="n">Foundation</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// clang -fmodules -F/System/Library/PrivateFrameworks -framework SkyLight -o headroom headroom.m &amp;&amp; ./headroom
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="kt">int</span> <span class="nf">SLSMainConnectionID</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="n">CFTypeRef</span> <span class="nf">SLSDisplayGetCurrentHeadroom</span><span class="p">(</span><span class="kt">int</span> <span class="n">did</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">int</span> <span class="n">MAIN_DISPLAY_ID</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">**</span> <span class="n">argv</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">cid</span> <span class="o">=</span> <span class="n">SLSMainConnectionID</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">NSLog</span><span class="p">(</span><span class="s">@&#34;SLSMainConnectionID: %d&#34;</span><span class="p">,</span> <span class="n">cid</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">CFTypeRef</span> <span class="n">headroom</span> <span class="o">=</span> <span class="n">SLSDisplayGetCurrentHeadroom</span><span class="p">(</span><span class="n">MAIN_DISPLAY_ID</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">NSLog</span><span class="p">(</span><span class="s">@&#34;SLSMainConnectionID: %@&#34;</span><span class="p">,</span> <span class="n">headroom</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="other-ideas">Other ideas</h2>
<h3 id="streaming-to-a-dummy">Streaming to a dummy</h3>
<p>While discussing this matter with István Tóth, the developer of <a href="https://github.com/waydabber/BetterDummy/"  target="_blank" rel="noopener" >BetterDummy</a>, he came up with an interesting idea.</p>
<ol>
<li>Create a <code>CGVirtualDisplay</code> with the same size as the built-in display</li>
<li>Tone map the SDR contents of the built-in display to 1000nits HDR video</li>
<li><code>CGDisplayStream</code> that video to the virtual display</li>
<li>Move the virtual display to the built-in display coordinates and use that as the main display</li>
</ol>
<p>The streaming part already works in the <a href="https://github.com/waydabber/BetterDummy/releases/tag/v1.1.0-beta1"  target="_blank" rel="noopener" >latest Beta of BetterDummy</a> and seems pretty fast as well. But adding tone mapping might cause this to be too resource intensive to be used.</p>
<h3 id="using-private-symbols">Using private symbols</h3>
<p>I think linking can be done against private symbols using memory offsets, I remember doing something like that 8 years ago at BitDefender, while trying to use the unexported <code>_decrypt</code> and <code>_generate_domain</code> methods of some DGA malware.</p>
<p>But the <code>dyld_shared_cache</code> model of macOS is something new to me and I don&rsquo;t have enough knowledge to be able to do that right now.</p>
<p>If someone has any idea how this can be achieved, I&rsquo;d be glad if you could send me a hint through the <a href="/contact" >Contact page</a>.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>Why aren&#39;t the most useful Mac apps on the App Store?</title>
      <link>https://alinpanaitiu.com/blog/apps-outside-app-store/</link>
      <pubDate>Fri, 03 Dec 2021 19:28:39 +0200</pubDate>
      <author>alin@panaitiu.com (Alin Panaitiu)</author>
      <guid>https://alinpanaitiu.com/blog/apps-outside-app-store/</guid>
      <description>
        <![CDATA[<p>Let’s set the stage first. So, it’s Tuesday night and I’m <code>Command</code> <code>Tab</code>-ing my way through 10 different apps, some with 3-4 windows, while trying to patch bugs in <strong>


<img
    src="https://alinpanaitiu.comimages/icons/lunar_16@1x.webp#x16"
    alt="Lunar Icon"
    
    
     height="16"
/> <a href="https://lunar.fyi"  target="_blank" rel="noopener" >Lunar</a></strong> faster than the users can submit the reports. I’m definitely failing.</p>
<p>I feel my brain pulsing and my ring finger going numb on the <code>Tab</code> key. I stop switching apps and just stare at the Xcode window, containing what I knew was Swift code but looked like gibberish right now.</p>
<p><em>“Feels like burnout”</em> I think. Wasn’t that what I ran away from when I quit my job to make apps for a living?</p>
<hr>
<p>I heard a joke recently:</p>


<details style="margin-top: 0">
    <summary><b>Show joke</b></summary>
    <img src="https://files.alinpanaitiu.com/9to5-joke.webp" alt="Didn't want a 9 to 5 job, now I work 24/7"/>
</details>

<p>It’s probably only funny for a small group of workaholics, but the reality of those words struck me in the middle of the hysterical laughter I was trying to stop.</p>
<p>Why am I still developing this app?</p>
<p>Why am I adding all the features the users are asking for, then deal with the flood of frustrated emails saying <em>“what an overcomplicated stupid app, I just want to make my screen brighter!!”</em>, then try to hide advanced features to make it simpler, then get assaulted with the confused <em>“I can’t change volume anymore fix this ASAP!!!”</em> because UI changes can very easily introduce bugs by simply forgetting to bind a slider to a value, then get back to scotch taping broken parts slower than the users can report them?</p>
<p>Those features should have probably been their own independent app.</p>
<p>I start to feel my fingers again, press <code>Command</code> <code>Tab</code> once more, and while looking at the list of app icons I realise something.</p>
<p>Maybe pressing <code>Tab</code> 4-5 times while visually assessing if the selected app icon is the one I want to focus, isn’t the best solution for this kind of workflow.</p>
<p>So what does my brain do when I feel burnt out? Gives me ideas for even more apps…</p>
<h2 id="rcmd">rcmd</h2>
<p>That’s how the idea of <strong>


<img
    src="https://alinpanaitiu.comimages/icons/rcmd_16@1x.webp#x16"
    alt="rcmd Icon"
    
    
     height="16"
/> <a href="https://lowtechguys.com/rcmd/"  target="_blank" rel="noopener" >rcmd</a></strong> began. We have two Command keys on a Mac keyboard, and the right hand side one is almost never used. What if I use it exclusively for switching apps?</p>














<a href="https://alinpanaitiu.comimages/dynamic-rcmd.jpg">
  <figure>
    <figcaption>rcmd app screenshot</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/dynamic-rcmd.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/dynamic-rcmd.jpg"
        alt="rcmd app screenshot"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>When I used Windows for reverse engineering malware, I liked switching apps using <code>Win</code> + <code>Number</code> where the number meant the position of the app icon in the taskbar. I didn’t like counting apps however.</p>
<p>Using the app name felt the most natural. I remembered using <a href="https://contexts.co/"  target="_blank" rel="noopener" >Contexts</a> for a while, which provides a Spotlight like search bar for fuzzy searching your running apps. But that needed a bit more key presses than I wanted <em>(that is <strong>1</strong>)</em> and more attention than I wanted to give <em>(which is none)</em>.</p>
<p>My idea sounded a bit simpler: <code>Right Command</code> + <code>the first letter of the app name</code></p>
<p>So simple that people were offended by it&hellip;</p>
<p>


<img
    src="https://files.alinpanaitiu.com/hn-comment-rcmd-price-offended.webp"
    alt="hacker news comment screenshot where someone is offended by the price"
    
    
    
/></p>
<p>I pitched this idea to <strong>Ovidiu Rusu</strong>, a very good friend of mine, who surprisingly seemed to have the same need as me. We created the first prototype in about a week <em>(icons and graphics take so much time…)</em>  and started using it in our day to day work to see if it made sense.</p>
<p>In less than a day, rcmd became so ingrained in our app switching that we got incredibly annoyed when we had to quit the app for recompiling and debugging. We just kept pressing <code>Right Command</code> <code>X</code> and staring at the screen like complete idiots, not understanding why Xcode wasn&rsquo;t being focused.</p>
<hr>
<p>What most people overlook when they have a simple idea is that 80% of the effort goes into handling edge cases that are not visible in the original idea.</p>
<p>Just for this simple app we had to solve the following problems:</p>
<ul>
<li>What do I do when there are multiple running apps with the same first letter?
<ul>
<li>How do I decide which one gets priority so that I meet user expectations?</li>
<li>How do I get to the other apps with less priority in as few key presses as possible?</li>
</ul>
</li>
<li>How do I persist key assignments if necessary?</li>
<li>What if the user assigned the key to an app that’s no longer running?
<ul>
<li>Should I launch the app when the key is pressed?</li>
<li>Where is the app located?</li>
</ul>
</li>
<li>There are dozens of running apps while only about 10% of them are actual apps launched by the user. How do I filter those out? The others could be:
<ul>
<li>Menu bar apps <em>(e.g. Alfred, Lunar etc)</em></li>
<li>OS services <em>(e.g. Spotlight, Siri etc.)</em></li>
<li>Daemons <em>(e.g. CoreAudio etc)</em></li>
</ul>
</li>
<li><strong>How do I only listen to the Right Command key?</strong></li>
</ul>
<p>This last question is what led me to write this article. It turned out we needed to do quite a few hacks if we wanted to publish this app in the App Store.</p>
<h2 id="the-sandbox">The Sandbox</h2>
<p>Every app that is submitted to the App Store must be compiled to run within a sandbox. This means that the app will run in a container which will have the same structure as your home directory, but with mostly empty folders.<br>
The sandbox also limits what APIs you can use, and which system components you can communicate with.</p>
<p>The defacto way of reacting to <code>Right Command</code> + <code>some other key</code> is to monitor all key events <em>(yes, just like a keylogger)</em>, and discard events that don’t contain the Right Command modifier flag.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">extension</span> <span class="nc">NSEvent</span><span class="p">.</span><span class="n">ModifierFlags</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kd">static</span> <span class="kd">let</span> <span class="nv">rightCommand</span> <span class="p">=</span> <span class="n">NSEvent</span><span class="p">.</span><span class="n">ModifierFlags</span><span class="p">(</span><span class="n">rawValue</span><span class="p">:</span> <span class="nb">UInt</span><span class="p">(</span><span class="n">NX_DEVICERCMDKEYMASK</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">NSEvent</span><span class="p">.</span><span class="n">addGlobalMonitorForEvents</span><span class="p">(</span><span class="n">matching</span><span class="p">:</span> <span class="p">.</span><span class="n">keyDown</span><span class="p">)</span> <span class="p">{</span> <span class="n">event</span> <span class="k">in</span>
</span></span><span class="line"><span class="cl">    <span class="k">guard</span> <span class="n">event</span><span class="p">.</span><span class="n">modifierFlags</span><span class="p">.</span><span class="bp">contains</span><span class="p">(.</span><span class="n">rightCommand</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// do your thing</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Easy peasy, right? Well no, because that’s <a href="https://stackoverflow.com/questions/32116095/how-to-use-accessibility-with-sandboxed-app"  target="_blank" rel="noopener" >not allowed on the App Store</a>.</p>
<p>To use that API you need to first request <strong>Accessibility Permissions</strong> from the user. Those permissions are <strong>prohibited inside the Sandbox</strong>, because with those permissions, an app would be able to do all kinds of nasty stuff:</p>
<ul>
<li>Log all your key presses <em>(including passwords if you aren’t using a Secure Input field)</em></li>
<li>Extract text from all the running apps</li>
<li>Click on buttons inside other apps</li>
<li>Write text in fields or send key combinations to the system</li>
<li>Render elements like buttons and tooltips over the interface of other apps</li>
</ul>
<p>Those are perfectly reasonable things in the context of assistive software, because you need the computer to do stuff for you when you aren’t able to use a keyboard or a mouse/trackpad.</p>
<p>And you need the computer to read out text from other apps, or show choice buttons which you can trigger with your voice.</p>
<hr>
<p><a href="#the-app-store-review" >Technical content ahead. Click to skip this section if you&rsquo;re not interested in macOS internals.</a></p>
<p>But for rcmd’s use case, we’re restricted to APIs that don’t require these permissions. APIs so old that 64-bit wasn’t even a thing when they launched and they require passing C function pointers instead of our beloved powerful Swift closures.</p>
<p>That’s the <a href="https://en.wikipedia.org/wiki/Carbon_%28API%29"  target="_blank" rel="noopener" >Carbon API</a> and it goes a little something like this:</p>
<h4 id="registering-a-commandr-hotkey">Registering a Command+R hotkey</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">// Install the key event handler</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nv">pressedEventType</span> <span class="p">=</span> <span class="n">EventTypeSpec</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">pressedEventType</span><span class="p">.</span><span class="n">eventClass</span> <span class="p">=</span> <span class="n">OSType</span><span class="p">(</span><span class="n">kEventClassKeyboard</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">pressedEventType</span><span class="p">.</span><span class="n">eventKind</span> <span class="p">=</span> <span class="n">OSType</span><span class="p">(</span><span class="n">kEventHotKeyPressed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">InstallEventHandler</span><span class="p">(</span><span class="n">GetEventDispatcherTarget</span><span class="p">(),</span> <span class="p">{</span> <span class="kc">_</span><span class="p">,</span> <span class="n">inEvent</span><span class="p">,</span> <span class="kc">_</span> <span class="p">-&gt;</span> <span class="n">OSStatus</span> <span class="k">in</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">handlePressedKeyboardEvent</span><span class="p">(</span><span class="n">inEvent</span><span class="p">!)</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="mi">1</span><span class="p">,</span> <span class="p">&amp;</span><span class="n">pressedEventType</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Register the hotkey</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">hotKeyId</span> <span class="p">=</span> <span class="n">EventHotKeyID</span><span class="p">(</span><span class="n">signature</span><span class="p">:</span> <span class="n">UTGetOSTypeFromString</span><span class="p">(</span><span class="s">&#34;some-unique-identifier&#34;</span> <span class="k">as</span> <span class="n">CFString</span><span class="p">),</span> <span class="n">id</span><span class="p">:</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nv">carbonHotKey</span><span class="p">:</span> <span class="n">EventHotKeyRef</span><span class="p">?</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">RegisterEventHotKey</span><span class="p">(</span><span class="nb">UInt32</span><span class="p">(</span><span class="n">kVK_ANSI_R</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">UInt32</span><span class="p">(</span><span class="n">cmdKey</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">hotKeyId</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">GetEventDispatcherTarget</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                    <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="p">&amp;</span><span class="n">carbonHotKey</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Handle the event</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handlePressedKeyboardEvent</span><span class="p">(</span><span class="kc">_</span> <span class="n">event</span><span class="p">:</span> <span class="n">EventRef</span><span class="p">)</span> <span class="p">-&gt;</span> <span class="n">OSStatus</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="bp">assert</span><span class="p">(</span><span class="nb">Int</span><span class="p">(</span><span class="n">GetEventClass</span><span class="p">(</span><span class="n">event</span><span class="p">))</span> <span class="p">==</span> <span class="n">kEventClassKeyboard</span><span class="p">,</span> <span class="s">&#34;Unknown event class&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nv">hotKeyId</span> <span class="p">=</span> <span class="n">EventHotKeyID</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="kd">let</span> <span class="nv">error</span> <span class="p">=</span> <span class="n">GetEventParameter</span><span class="p">(</span><span class="n">event</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                  <span class="n">EventParamName</span><span class="p">(</span><span class="n">kEventParamDirectObject</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                                  <span class="n">EventParamName</span><span class="p">(</span><span class="n">typeEventHotKeyID</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                                  <span class="kc">nil</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                  <span class="n">MemoryLayout</span><span class="p">&lt;</span><span class="n">EventHotKeyID</span><span class="p">&gt;.</span><span class="n">size</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                  <span class="kc">nil</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                  <span class="p">&amp;</span><span class="n">hotKeyId</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">guard</span> <span class="n">error</span> <span class="p">==</span> <span class="n">noErr</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="n">error</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="bp">assert</span><span class="p">(</span><span class="n">hotKeyId</span><span class="p">.</span><span class="n">signature</span> <span class="p">==</span> <span class="n">UTGetOSTypeFromString</span><span class="p">(</span><span class="s">&#34;some-unique-identifier&#34;</span> <span class="k">as</span> <span class="n">CFString</span><span class="p">),</span> <span class="s">&#34;Invalid hot key id&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="n">GetEventKind</span><span class="p">(</span><span class="n">event</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">EventParamName</span><span class="p">(</span><span class="n">kEventHotKeyPressed</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// do your thing.. eventually</span>
</span></span><span class="line"><span class="cl">    <span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="bp">assert</span><span class="p">(</span><span class="kc">false</span><span class="p">,</span> <span class="s">&#34;Unknown event kind&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">noErr</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><em><strong>Not so pretty as the <code>NSEvent</code> method, but does the job. Kind of.</strong></em></p>
<p>You see, that beautiful code macaroni above only lets us listen to <code>Any Command</code> + <code>R</code>, not specifically the <code>Right Command</code>. There&rsquo;s no way to pass something like <code>rightCmdKey</code> into <code>RegisterEventHotKey</code>.</p>
<p>A workaround I found for this was:</p>
<ul>
<li>Listen for <code>flagsChanged</code></li>
<li>Set a global boolean to <code>true</code> when the there&rsquo;s a <code>rightCommand</code> modifier</li>
<li>Discard the event if the boolean is <code>false</code></li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nv">rcmd</span> <span class="p">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">NSEvent</span><span class="p">.</span><span class="n">addGlobalMonitorForEvents</span><span class="p">(</span><span class="n">matching</span><span class="p">:</span> <span class="p">.</span><span class="n">flagsChanged</span><span class="p">)</span> <span class="p">{</span> <span class="n">event</span> <span class="k">in</span>
</span></span><span class="line"><span class="cl">    <span class="n">rcmd</span> <span class="p">=</span> <span class="n">event</span><span class="p">.</span><span class="n">modifierFlags</span><span class="p">.</span><span class="bp">contains</span><span class="p">(.</span><span class="n">rightCommand</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">handleHotkey</span><span class="p">(</span><span class="n">key</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">guard</span> <span class="n">rcmd</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">focusApp</span><span class="p">(</span><span class="n">with</span><span class="p">:</span> <span class="n">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><em>Doing this reminded me of the days I worked with Rust, and how wonderfully impossible a task like this would be. I don&rsquo;t think I&rsquo;m touching it again, I like my global atomic booleans.</em></p>
<p>Now the weirdest limitation hits me. There&rsquo;s no way to discard a hotkey event and forward it back to the system so it can use it for the next handler.</p>
<p>Say I register <code>Command</code> <code>C</code> and I only want to do something when <code>Right Command</code> is held. If I do nothing when <code>Left Command</code> is held, then you can&rsquo;t copy text anymore using <code>Command</code> <code>C</code>.</p>
<p>I tried returning the inappropriately named <code>OSStatus(eventNotHandledErr)</code> but the event still doesn&rsquo;t return to the handler chain.</p>
<p><strong>At this point we seriously considered dropping the App Store idea and just going the self publishing route.</strong></p>
<p>But just thinking what we would have to do for that triggered something akin to PTSD.</p>
<p>Here&rsquo;s a list with what I can remember off the top of my head from Lunar:</p>
<ul>
<li>Implement the Paddle SDK for licensing</li>
<li>Add Sentry for error reporting</li>
<li>Lose the useful App Store analytics</li>
<li>Lose ratings and reviews</li>
<li>Add Sparkle for auto-updating
<ul>
<li>Generate signing keys</li>
<li>Create DMG with the app</li>
<li>Sign every single update</li>
<li>Try not to lose the signing key</li>
<li>Generate the appcast XML</li>
<li>Host that appcast somewhere</li>
</ul>
</li>
<li>Host the app bundle for download somewhere</li>
</ul>
<p>Finding yet another workaround seemed much easier.</p>
<p>Thankfully it really was easy. It turns out that <code>RegisterEventHotKey</code> is plenty fast. So fast that we were able to register the hotkeys only when <code>Right Command</code> was being held, and unregister them when the key was released.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">import</span> <span class="nc">Atomics</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nv">_rcmd</span> <span class="p">=</span> <span class="n">ManagedAtomic</span><span class="p">&lt;</span><span class="nb">Bool</span><span class="p">&gt;(</span><span class="kc">false</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nv">rcmd</span><span class="p">:</span> <span class="nb">Bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">get</span> <span class="p">{</span> <span class="n">_rcmd</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="n">ordering</span><span class="p">:</span> <span class="p">.</span><span class="n">relaxed</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="kr">set</span> <span class="p">{</span> <span class="n">_rcmd</span><span class="p">.</span><span class="n">store</span><span class="p">(</span><span class="n">newValue</span><span class="p">,</span> <span class="n">ordering</span><span class="p">:</span> <span class="p">.</span><span class="n">sequentiallyConsistent</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">NSEvent</span><span class="p">.</span><span class="n">addGlobalMonitorForEvents</span><span class="p">(</span><span class="n">matching</span><span class="p">:</span> <span class="p">.</span><span class="n">flagsChanged</span><span class="p">)</span> <span class="p">{</span> <span class="n">event</span> <span class="k">in</span>
</span></span><span class="line"><span class="cl">    <span class="n">rcmd</span> <span class="p">=</span> <span class="n">event</span><span class="p">.</span><span class="n">modifierFlags</span><span class="p">.</span><span class="bp">contains</span><span class="p">(.</span><span class="n">rightCommand</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">rcmd</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">registerHotkeys</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">unregisterHotkeys</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h2 id="the-app-store-review">The App Store review</h2>
<p>Now rcmd was ready for publishing on the App Store.</p>
<p>There was one little thing that bothered me though. I usually keep 4-5 separate projects open in Sublime Text, each with its own window. Because of the sandbox, there&rsquo;s no way to get a list of windows for an app and, say, focus a specific one, or cycle between them.</p>
<p>But I found a little gem while I was customising my fork of <a href="https://github.com/koekeishiya/yabai"  target="_blank" rel="noopener" >yabai</a>, a way to trigger Exposé for a single app:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="n">CoreDockSendNotification</span><span class="p">(</span><span class="s">&#34;com.apple.expose.front.awake&#34;</span> <span class="k">as</span> <span class="n">CFString</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div>













<a href="https://alinpanaitiu.comimages/app-window-expose.jpg">
  <figure>
    <figcaption>app window expose screenshot</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/app-window-expose.jpg@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/app-window-expose.jpg 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/app-window-expose.jpg 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/app-window-expose.jpg 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/app-window-expose.jpg 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/app-window-expose.jpg 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/app-window-expose.jpg 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/app-window-expose.jpg 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/app-window-expose.jpg 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/app-window-expose.jpg 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/app-window-expose.jpg 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/app-window-expose.jpg 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/app-window-expose.jpg 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/app-window-expose.jpg 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/app-window-expose.jpg"
        alt="app window expose screenshot"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>We decided to show Exposé if for example you press <code>rcmd</code> <code>s</code> while Sublime Text is already focused. It was good enough for us.</p>
<p>Not for the App Store reviewer though.</p>














<a href="https://alinpanaitiu.comimages/coredock-review.png">
  <figure>
    <figcaption>app store review rejecting the expose feature</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/coredock-review.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/coredock-review.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/coredock-review.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/coredock-review.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/coredock-review.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/coredock-review.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/coredock-review.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/coredock-review.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/coredock-review.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/coredock-review.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/coredock-review.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/coredock-review.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/coredock-review.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/coredock-review.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/coredock-review.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/coredock-review.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/coredock-review.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/coredock-review.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/coredock-review.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/coredock-review.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/coredock-review.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/coredock-review.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/coredock-review.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/coredock-review.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/coredock-review.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/coredock-review.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/coredock-review.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/coredock-review.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/coredock-review.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/coredock-review.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/coredock-review.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/coredock-review.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/coredock-review.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/coredock-review.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/coredock-review.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/coredock-review.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/coredock-review.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/coredock-review.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/coredock-review.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/coredock-review.png"
        alt="app store review rejecting the expose feature"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<p>I knew private and undocumented APIs are not seen well on the App Store. But I had no idea they will guarantee a rejection.</p>
<h2 id="free-trials">Free Trials</h2>
<p><strong>I like breaking the norm with my creations.</strong> Some of them will be flukes, some will be criticised into oblivion, but a small number of them might turn out to be something a lot of people wanted but didn&rsquo;t know they needed.</p>
<p>rcmd is one of those things: a bit quirky, unique in its approach, and incredibly useful for a specific group of people.</p>
<p>That is also its weak point though. It&rsquo;s hard to communicate this usefulness without being able to try the app first. But as it turns out, the App Store doesn&rsquo;t provide any support for creating a <em>free XX-day trial</em> before buying an app.</p>
<p><strong>Free trials for non-subscription apps have been allowed since mid-2018 on the App Store</strong>, and are supposed to be implemented using in-app purchases. Unfortunately, this approach has a lot of inconveniences which are very well detailed in this article: <a href="https://bitsplitting.org/2018/06/06/ersatz-free-trials/"  target="_blank" rel="noopener" >Ersatz Free Trials | Bitsplitting.org</a></p>
<p>These are the biggest shortcomings for my case:</p>
<ul>
<li>The trial doesn&rsquo;t start immediately
<ul>
<li>The users have to be nagged with a custom <em>Activate Trial</em> UI to start the trial manually</li>
<li>Starting the trial means <strong>buying a $0.00 in-app purchase</strong></li>
<li>If the users skip the trial UI out of frustration, they&rsquo;ll be left with a dumbed down version of the app, possibly missing the functionality they downloaded it for</li>
</ul>
</li>
<li>You have to create your own UI for starting a trial, buying the full app after the trial ends, showing the trial status etc.</li>
<li>You have to provide some kind of limited functionality if the trial ends
<ul>
<li>In the context of simple and small apps like rcmd, this doesn&rsquo;t really make sense</li>
</ul>
</li>
</ul>
<p>I tried a few dozen apps on the App Store and I couldn&rsquo;t find a single one offering a free trial for a non-subscription purchase using the above method.</p>
<p>Having to pay upfront is steering away a lot of possible users, but with all that bad UX, we decided to not implement any free trial and just sell the app for a one-time fair price.</p>
<hr>
<p><em>3 years ago, I would have probably chosen to make the app open source and give it away for free, just like I did with Lunar.</em></p>
<p><em>I would have thought:</em></p>
<blockquote>
<p><em>I&rsquo;m making a ton of money at this company, what I would get by selling a small app would be peanuts anyway.</em></p>
</blockquote>
<p><em>Only recently I realised that this approach kept me dependent on having a job where I click-clack useless programs 8 hours a day, only to get 1-2 hours after work for my projects, and sacrifice my health and sanity in the process.</em></p>
<p><em>In my whole 7-year career as a professional API Glue Technician and experienced Wheel Reinventer, I never did anything remotely as useful as even the simplest app I can code and publish in 2 months right now. At those companies, most of my work was scraped anyway when the redesign period of the year came.</em></p>
<p><em>So I&rsquo;d rather have those peanuts please.</em></p>
<hr>
<h2 id="everyones-choosing-to-be-left-out">Everyone&rsquo;s choosing to be left out</h2>


<style type="text/css">
    #everyones-choosing-to-be-left-out ~ h3 {
        margin-bottom: 0;
        padding-bottom: 0;
        padding-top: 3rem;
        display: flex;
        justify-content: start;
        align-items: center;
    }
    #everyones-choosing-to-be-left-out ~ h3 > img {
        border-radius: 0;
        filter: none;
        margin: 0;
        display: inline;
        object-fit: cover;
        margin-right: 8px;
        height: 1.5rem;
        filter: drop-shadow(0px 2px 3px rgba(10,0,20,0.3));
    }
</style>

<p>Now, with so many limitations, I think we can take a fair guess at why most indie developers choose to distribute their app outside the App Store.</p>
<p>Here are some of the apps I find most useful, and what I think is the main reason for them not being in the App Store:</p>
<h3 id="alfred-iconimagesiconsalfredwebpx20-alfredhttpswwwalfredappcom">


<img
    src="https://alinpanaitiu.comimages/icons/alfred.webp#x20"
    alt="Alfred Icon"
    
    
     height="20"
/> <a href="https://www.alfredapp.com/"  target="_blank" rel="noopener" >Alfred</a></h3>
<p>The app&rsquo;s main functionality (searching the filesystem) needs <strong>Full Disk Access</strong> permissions which are not allowed inside the sandbox.</p>
<p>It also uses <strong>Accessibility Permissions</strong> for auto-expanding snippets and other custom workflows.</p>
<h3 id="bettertouchtool-iconimagesiconsbttwebpx20-bettertouchtoolhttpsfolivoraai">


<img
    src="https://alinpanaitiu.comimages/icons/btt.webp#x20"
    alt="BetterTouchTool Icon"
    
    
     height="20"
/> <a href="https://folivora.ai/"  target="_blank" rel="noopener" >BetterTouchTool</a></h3>
<p>Capturing and responding to all kinds of keyboard and trackpad events needs <strong>Accessibility Permissions</strong>.</p>
<p>The app also encapsulates the older BetterSnapTool utility for snapping windows to a grid. Resizing windows requires the same permissions.</p>
<h3 id="karabiner-elements-iconimagesiconskarabinerwebpx20-karabiner-elementshttpskarabiner-elementspqrsorg">


<img
    src="https://alinpanaitiu.comimages/icons/karabiner.webp#x20"
    alt="Karabiner-Elements Icon"
    
    
     height="20"
/> <a href="https://karabiner-elements.pqrs.org/"  target="_blank" rel="noopener" >Karabiner-Elements</a></h3>
<p>Reacting to and changing keyboard input in realtime needs a special keyboard driver which is only allowed by Apple on a case by case basis. You have to <a href="https://developer.apple.com/documentation/driverkit/requesting_entitlements_for_driverkit_development"  target="_blank" rel="noopener" >request DriverKit entitlements from Apple</a>, and they have to deem you worthy of those entitlements.</p>
<p>Needless to say, they won&rsquo;t give hardware driver entitlements for a software app mimicking a keyboard.</p>
<h3 id="sublime-text-iconimagesiconssublimewebpx20-sublime-texthttpswwwsublimetextcom">


<img
    src="https://alinpanaitiu.comimages/icons/sublime.webp#x20"
    alt="Sublime Text Icon"
    
    
     height="20"
/> <a href="https://www.sublimetext.com/"  target="_blank" rel="noopener" >Sublime Text</a></h3>
<p><strong>Full Disk Access</strong> is probably the biggest requirement here.</p>
<p>Of course, there are other code editors on the App Store like <a href="https://apps.apple.com/us/app/bbedit/id404009241?mt=12"  target="_blank" rel="noopener" >BBEdit</a> but they have this initial phase where you have to manually give them access to your <code>/</code> (root) directory.</p>
<p>













<a href="https://alinpanaitiu.comimages/bbedit-sandbox-access-dialog.png">
  <figure>
    <figcaption>bbedit sandbox access dialog</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bbedit-sandbox-access-dialog.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/bbedit-sandbox-access-dialog.png"
        alt="bbedit sandbox access dialog"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>
<br>














<a href="https://alinpanaitiu.comimages/bbedit-allow-access.png">
  <figure>
    <figcaption>bbedit allow access</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/bbedit-allow-access.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/bbedit-allow-access.png"
        alt="bbedit allow access"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>
</p>
<p>Compared to Sublime Text&rsquo;s <em><strong>launch and edit instantly</strong></em> first time experience, I feel this is a bit annoying. I&rsquo;m pretty sure this confuses a lot of first time users, and they will probably blame the developer, not knowing that this is the only way to access files from the Sandbox.</p>
<h3 id="swish-iconimagesiconsswishwebpx20-swishhttpshighlyopinionatedcoswish">


<img
    src="https://alinpanaitiu.comimages/icons/swish.webp#x20"
    alt="Swish Icon"
    
    
     height="20"
/> <a href="https://highlyopinionated.co/swish/"  target="_blank" rel="noopener" >Swish</a></h3>
<p>Resizing windows, listening for global trackpad gestures, detecting titlebars, moving windows to other spaces/screens. All of these need <strong>Accessibility Permissions</strong>.</p>
<p>There&rsquo;s even an FAQ for that on their page:</p>
<blockquote>
<h3 id="why-is-swish-not-on-the-app-store">Why is Swish not on the App Store?</h3>
<p>Apple only allows sandboxed apps on the App Store. Swish needs to perform low-level system operations which prevent it from being sandboxed. <a href="https://stackoverflow.com/q/32116095"  target="_blank" rel="noopener" >Read more here</a>.</p>
</blockquote>
<h3 id="sip-iconimagesiconssipwebpx20-siphttpssipappio">


<img
    src="https://alinpanaitiu.comimages/icons/sip.webp#x20"
    alt="Sip Icon"
    
    
     height="20"
/> <a href="https://sipapp.io/"  target="_blank" rel="noopener" >Sip</a></h3>
<p>As outlined in their 2017 article, <a href="https://sipapp.medium.com/moving-from-mac-app-store-b41c9e2f53e8"  target="_blank" rel="noopener" >Moving from Mac App Store</a>, the sandbox limitation is the primary reason</p>
<h3 id="cleanshot-x-iconimagesiconscleanshotwebpx20-cleanshot-xhttpscleanshotcom">


<img
    src="https://alinpanaitiu.comimages/icons/cleanshot.webp#x20"
    alt="CleanShot X Icon"
    
    
     height="20"
/> <a href="https://cleanshot.com/"  target="_blank" rel="noopener" >CleanShot X</a></h3>
<p>Their Screen Recording feature has three very useful functions:</p>
<ul>
<li><strong>Audio recording</strong> (this needs installing an <strong>audio loopback</strong> device)</li>
<li><strong>Pressed keys overlay</strong> (needs <strong>Input Monitoring</strong> permissions)</li>
<li><strong>Select single window</strong> (needs <strong>Accessibility Permissions</strong> for getting the window position and size)</li>
</ul>
<h3 id="sketch-iconimagesiconssketchwebpx20-sketchhttpswwwsketchcom">


<img
    src="https://alinpanaitiu.comimages/icons/sketch.webp#x20"
    alt="Sketch Icon"
    
    
     height="20"
/> <a href="https://www.sketch.com/"  target="_blank" rel="noopener" >Sketch</a></h3>
<p>Honestly, I&rsquo;m not sure about this one. The App Store is full of image editors and graphic content creation tools.</p>
<p>I thing the unique pricing model is something they would have a hard time implementing on the App Store.</p>
<p><em>The unique pricing model of Sketch</em><br>














<a href="https://alinpanaitiu.comimages/sketch-pricing.png">
  <figure>
    <figcaption>sketch pricing model</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sketch-pricing.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sketch-pricing.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sketch-pricing.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sketch-pricing.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sketch-pricing.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sketch-pricing.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sketch-pricing.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sketch-pricing.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sketch-pricing.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sketch-pricing.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sketch-pricing.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sketch-pricing.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sketch-pricing.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sketch-pricing.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sketch-pricing.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sketch-pricing.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sketch-pricing.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sketch-pricing.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sketch-pricing.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sketch-pricing.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sketch-pricing.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sketch-pricing.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sketch-pricing.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sketch-pricing.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sketch-pricing.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sketch-pricing.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sketch-pricing.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sketch-pricing.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sketch-pricing.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sketch-pricing.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sketch-pricing.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sketch-pricing.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sketch-pricing.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sketch-pricing.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sketch-pricing.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sketch-pricing.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sketch-pricing.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sketch-pricing.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sketch-pricing.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/sketch-pricing.png"
        alt="sketch pricing model"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>
</p>
<h3 id="parallels-desktop-iconimagesiconsparallelswebpx20-parallels-desktophttpswwwparallelscomeuproductsdesktop">


<img
    src="https://alinpanaitiu.comimages/icons/parallels.webp#x20"
    alt="Parallels Desktop Icon"
    
    
     height="20"
/> <a href="https://www.parallels.com/eu/products/desktop/"  target="_blank" rel="noopener" >Parallels Desktop</a></h3>
<p>They actually have an App Store edition, but it&rsquo;s severely limited.</p>
<p>Sharing things between the host and the VM is probably the largest functionality affected by the sandbox.</p>
<p>They provide a table with everything that&rsquo;s missing in their App Store version of the app: <a href="https://kb.parallels.com/123796"  target="_blank" rel="noopener" >KB Parallels: What is the difference between Parallels Desktop App Store Edition and Standard Edition?</a></p>
<h3 id="lunar-iconimagesiconslunarwebpx20-lunarhttpslunarfyi">


<img
    src="https://alinpanaitiu.comimages/icons/lunar.webp#x20"
    alt="Lunar Icon"
    
    
     height="20"
/> <a href="https://lunar.fyi/"  target="_blank" rel="noopener" >Lunar</a></h3>
<p>Low-level communication with monitors is only possible by using a lot of private and reverse engineered APIs (<code>IOKit</code>, <code>DisplayServices</code>, <code>IOAVService</code> etc.)</p>
<p><strong>Accessibility Permissions</strong> are also needed for listening and reacting to brightness and volume key events.</p>
<p>Because of the sandbox, the <a href="https://apps.apple.com/us/app/lunar-lite-screen-brightness/id1594174299"  target="_blank" rel="noopener" >lite App Store version of Lunar</a> only supports software dimming and can only react to F1/F2 keys.</p>
<h3 id="soulver-iconimagesiconssoulverwebpx20-soulverhttpssoulverapp">


<img
    src="https://alinpanaitiu.comimages/icons/soulver.webp#x20"
    alt="Soulver Icon"
    
    
     height="20"
/> <a href="https://soulver.app/"  target="_blank" rel="noopener" >Soulver</a></h3>
<p>I think the <strong>free trial limitation</strong> is the only thing keeping such a self-contained app outside the App Store.</p>
<p>Soulver is incredibly complex and useful in its functionality, but I don&rsquo;t think too many people would splurge $35 on a notepad-calculator app without trying it first. It deserves every single dollar of that price, that I can say for sure.</p>
]]>
      </description>
    </item>
    
    <item>
      <title>The journey to controlling external monitors on M1 Macs</title>
      <link>https://alinpanaitiu.com/blog/journey-to-ddc-on-m1-macs/</link>
      <pubDate>Fri, 16 Jul 2021 18:39:37 +0300</pubDate>
      <author>alin@panaitiu.com (Alin Panaitiu)</author>
      <guid>https://alinpanaitiu.com/blog/journey-to-ddc-on-m1-macs/</guid>
      <description>
        <![CDATA[<p>One lazy evening in November 2020, I watched how Tim Cook announced a <strong>fanless</strong> MacBook Air with a CPU faster than the latest 16 inch MacBook, while my work-provided 15 inch 2019 MacBook Pro was slowly frying my lap and annoying my wife with its constant fan noise.</p>
<p>I had to get my hands on that machine. I also had the excuse that users of my app couldn&rsquo;t control their monitor brightness anymore, so I could justify the expense easily in my head.</p>
<p>So I got it! With long delays and convoluted delivery schemes because living in a country like Romania means incredibly high prices on everything Apple.</p>
<p>This already starts to sound like those happy stories about seeing how awesome M1 is, but it’s far from that.</p>
<p>This is a story about how getting an M1 made me quit my job, bang my head against numerous walls to figure out monitor support for it and turn an open source app into something that I can really live off without needing a &ldquo;real job&rdquo;.</p>
<h2 id="adjusting-monitor-brightness-on-intel-macs">Adjusting monitor brightness on Intel Macs</h2>
<p>I develop an app called <a href="https://lunar.fyi"  target="_blank" rel="noopener" >Lunar</a> that can adjust the real brightness, contrast and volume of monitors by sending <a href="https://en.wikipedia.org/wiki/Display_Data_Channel"  target="_blank" rel="noopener" >DDC</a> commands through the Mac GPU.</p>














<a href="https://alinpanaitiu.comimages/lunar-screenshot.png">
  <figure>
    <figcaption>screenshot of the Lunar app Display Settings</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/lunar-screenshot.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/lunar-screenshot.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/lunar-screenshot.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/lunar-screenshot.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/lunar-screenshot.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/lunar-screenshot.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/lunar-screenshot.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/lunar-screenshot.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/lunar-screenshot.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/lunar-screenshot.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/lunar-screenshot.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/lunar-screenshot.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/lunar-screenshot.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/lunar-screenshot.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/lunar-screenshot.png"
        alt="screenshot of the Lunar app Display Settings"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<h4 id="useful-links">Useful links</h4>
<ul>
<li><a href="https://www.ddcutil.com/bibliography/#i2c"  target="_blank" rel="noopener" >DDC and I²C</a></li>
<li><a href="http://boichat.ch/nicolas/ddcci/specs.html"  target="_blank" rel="noopener" >DDC/CI Specifications</a></li>
</ul>
<p>On Intel Macs this worked really well because macOS had some private APIs to find the framebuffer of a monitor, send data to it through I²C, and best of all, someone has already done the hard part in figuring this out in this <a href="https://github.com/kfix/ddcctl"  target="_blank" rel="noopener" >ddcctl utility</a>.</p>
<p>M1 Macs came with a different kernel, very similar to the iOS one. The previous APIs weren’t working anymore on the M1 GPU, the <code>IOFramebuffer</code> was now an <code>IOMobileFramebuffer</code> and the <code>IOI2C*</code> functions weren’t doing anything.</p>
<p>All of a sudden, I was getting countless emails, Twitter DMs and GitHub issues about how Lunar doesn’t work anymore on macOS Big Sur <em>(most M1 users were thinking the OS upgrade was causing this, disregarding the fact that they’re now using hardware and firmware that was never before seen on the Mac)</em></p>
<p>This was also a reality check for me. Without analytics, I had no idea that Lunar had so many active users!</p>
<h5 id="constructing-the-ddc-request">Constructing the DDC request</h5>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define BRIGHTNESS_CONTROL_ID 0x10
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="n">UInt8</span> <span class="n">brightness</span> <span class="o">=</span> <span class="mi">75</span><span class="p">;</span>  <span class="c1">// 75% brightness
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="n">IOI2CRequest</span> <span class="n">request</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">bzero</span><span class="p">(</span><span class="o">&amp;</span><span class="n">request</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">request</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">request</span><span class="p">.</span><span class="n">commFlags</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">request</span><span class="p">.</span><span class="n">sendAddress</span> <span class="o">=</span> <span class="mh">0x6E</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">request</span><span class="p">.</span><span class="n">sendTransactionType</span> <span class="o">=</span> <span class="n">kIOI2CSimpleTransactionType</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">request</span><span class="p">.</span><span class="n">sendBytes</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">UInt8</span> <span class="n">data</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="n">request</span><span class="p">.</span><span class="n">sendBuffer</span> <span class="o">=</span> <span class="p">(</span><span class="n">vm_address_t</span><span class="p">)</span><span class="o">&amp;</span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x51</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">data</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x84</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">data</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x03</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">data</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">BRIGHTNESS_CONTROL_ID</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">data</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="n">brightness</span> <span class="o">&gt;&gt;</span> <span class="mi">8</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">data</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="n">brightness</span> <span class="o">&amp;</span> <span class="mi">255</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">data</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x6E</span> <span class="o">^</span> <span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">^</span> <span class="n">data</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">^</span> <span class="n">data</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">^</span> <span class="n">data</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">^</span> <span class="n">data</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">^</span> <span class="n">data</span><span class="p">[</span><span class="mi">5</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">request</span><span class="p">.</span><span class="n">replyTransactionType</span> <span class="o">=</span> <span class="n">kIOI2CNoTransactionType</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">request</span><span class="p">.</span><span class="n">replyBytes</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h5 id="sending-the-data-through-ic">Sending the data through I²C</h5>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">io_service_t</span> <span class="n">framebuffer</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">CGSServiceForDisplayNumber</span><span class="p">(</span><span class="n">displayID</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">framebuffer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">io_service_t</span> <span class="n">interface</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">IOFBCopyI2CInterfaceForBus</span><span class="p">(</span><span class="n">framebuffer</span><span class="p">,</span> <span class="n">bus</span><span class="o">++</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">interface</span><span class="p">)</span> <span class="o">!=</span> <span class="n">KERN_SUCCESS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">IOI2CConnectRef</span> <span class="n">connect</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">IOI2CInterfaceOpen</span><span class="p">(</span><span class="n">interface</span><span class="p">,</span> <span class="n">kNilOptions</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">connect</span><span class="p">)</span> <span class="o">==</span> <span class="n">KERN_SUCCESS</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">IOI2CSendRequest</span><span class="p">(</span><span class="n">connect</span><span class="p">,</span> <span class="n">kNilOptions</span><span class="p">,</span> <span class="n">request</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">IOI2CInterfaceClose</span><span class="p">(</span><span class="n">connect</span><span class="p">,</span> <span class="n">kNilOptions</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">IOObjectRelease</span><span class="p">(</span><span class="n">interface</span><span class="p">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><hr>
<h2 id="hands-on-with-the-m1">Hands-on with the M1</h2>
<p>It was the last day of November. Winter was already coming. Days were cold and less than 10km away from my place you could take a walk through snowy forests.</p>
<p><em>snowy forests in Răcădău (Braşov, Romania)</em><br>














<a href="https://alinpanaitiu.comimages/snowy-forests.png">
  <figure>
    <figcaption>snowy forests in Brasov, Romania</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/snowy-forests.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/snowy-forests.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/snowy-forests.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/snowy-forests.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/snowy-forests.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/snowy-forests.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/snowy-forests.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/snowy-forests.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/snowy-forests.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/snowy-forests.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/snowy-forests.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/snowy-forests.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/snowy-forests.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/snowy-forests.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/snowy-forests.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/snowy-forests.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/snowy-forests.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/snowy-forests.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/snowy-forests.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/snowy-forests.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/snowy-forests.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/snowy-forests.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/snowy-forests.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/snowy-forests.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/snowy-forests.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/snowy-forests.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/snowy-forests.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/snowy-forests.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/snowy-forests.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/snowy-forests.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/snowy-forests.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/snowy-forests.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/snowy-forests.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/snowy-forests.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/snowy-forests.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/snowy-forests.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/snowy-forests.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/snowy-forests.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/snowy-forests.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/snowy-forests.png"
        alt="snowy forests in Brasov, Romania"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>
</p>
<p>But I was fortunate, as I had my trusty 2019 MacBook Pro to keep my hands warm while I was cranking code that will be obsolete in less than 6 months on my day job.</p>
<p>Just as the day turned into evening, the delivery guy called me about a laptop: <strong>the custom configured M1 MacBook Pro that costed as much as 7 junior developer monthly salaries has arrived!</strong></p>
<p>After charging the laptop to 100%, I started the installation of my enormous <a href="https://thoughtbot.com/blog/brewfile-a-gemfile-but-for-homebrew"  target="_blank" rel="noopener" >Brewfile</a> and left it on battery as an experiment. Meanwhile I kept working on the 2019 MacBook because my day job was also a night job when deadlines got tight.</p>
<p>Before I went to sleep, I wanted to test Lunar just to get an idea of what happens on M1. I launched it through Rosetta and the app window showed up as expected, every UI interaction worked normally but DDC was unresponsive. The monitor wasn’t being controlled in any way. I just hoped this was an easy fix and headed to bed.</p>
<h2 id="workarounds">Workarounds</h2>
<p>So it turns out the I/O structure is very different on M1 (more similar to iPhones and iPads than to previous Macs). There&rsquo;s no <code>IOFramebuffer</code> that we can call <code>IOFBCopyI2CInterfaceForBus</code> on. There&rsquo;s now an <code>IOMobileFramebuffer</code> in its place that has no equivalent function for getting an I²C bus from it.</p>
<p>After days of sifting through the I/O Registry trying to find a way to send I²C data to the monitor, I gave up and tried to find a workaround.</p>
<p>I realized I couldn’t work without Lunar being functional. I went back to doing the ritual I had to do in the first days I got my monitor and had no idea about DDC:</p>
<ul>
<li>Every evening I notice eye fatigue and a mild headache because the monitor is blinding me with its incredibly bright LED backlight <em>(might also be that I&rsquo;m reaching the 10th hour of working but who counts)</em></li>
<li>Go through nested menus with an annoying joystick to lower the brightness and contrast</li>
<li>Repeat about 3-5 times until I get too tired to do any more work</li>
<li>Wake up in the morning</li>
<li>Make coffee</li>
<li>Get anxious about how much work there is to be done to meet the deadline</li>
<li>Try to get back to work but now I can&rsquo;t see a thing on the monitor because it has the brightness and contrast set to almost 0 from the night before</li>
<li>Go through the same annoying menus to increase the brightness and contrast</li>
<li>Repeat throughout the day</li>
</ul>
<h3 id="gamma-tables">Gamma Tables</h3>
<p>One specific comment was becoming prevalent among Lunar users:</p>
<blockquote>
<p>QuickShade works for me on M1. Why can’t Lunar work?</p>
</blockquote>
<p><a href="https://apps.apple.com/ro/app/quickshade/id931571202?mt=12"  target="_blank" rel="noopener" >QuickShade</a> uses a black overlay with adjustable opacity to lower the image brightness. It can work on any Mac because it doesn’t depend on some private APIs to change the brightness of the monitor.</p>
<p><em>it also makes colors look more washed out in low brightness</em><br>


<video disablepictureinpicture autoplay loop muted playsinline style="width: 100%; margin: 10px auto; border-radius: 8px;" poster="https://alinpanaitiu.comimages/quickshade-comparison.webp" >
  <source src="/video/quickshade-comparison-small.mp4" type="video/mp4; codecs=avc1">
  <source src="/video/quickshade-comparison-small.webm" type="video/webm; codecs=vp9">
</video>
</p>
<p>Actually, unlike Lunar, QuickShade doesn’t change the monitor brightness at all.</p>
<p>QuickShade simulates a lower brightness by darkening the image using a fullscreen click-through black window that changes its opacity based on the brightness slider. The <strong>LED backlight</strong> of the monitor and the brightness value in its <strong>OSD</strong> stay the same.</p>
<p>This is by no means a bad critique of QuickShade. It is a simple utility that does its job very well. Some people don&rsquo;t even notice the difference between an overlay and real brightness adjustments that much so QuickShade might be a better choice for them.</p>
<p><em>LED monitor basic structure</em><br>














<a href="https://alinpanaitiu.comimages/led-panel-structure.png">
  <figure>
    <figcaption>LED panel structure</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/led-panel-structure.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/led-panel-structure.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/led-panel-structure.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/led-panel-structure.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/led-panel-structure.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/led-panel-structure.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/led-panel-structure.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/led-panel-structure.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/led-panel-structure.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/led-panel-structure.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/led-panel-structure.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/led-panel-structure.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/led-panel-structure.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/led-panel-structure.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/led-panel-structure.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/led-panel-structure.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/led-panel-structure.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/led-panel-structure.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/led-panel-structure.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/led-panel-structure.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/led-panel-structure.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/led-panel-structure.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/led-panel-structure.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/led-panel-structure.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/led-panel-structure.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/led-panel-structure.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/led-panel-structure.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/led-panel-structure.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/led-panel-structure.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/led-panel-structure.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/led-panel-structure.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/led-panel-structure.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/led-panel-structure.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/led-panel-structure.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/led-panel-structure.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/led-panel-structure.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/led-panel-structure.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/led-panel-structure.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/led-panel-structure.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/led-panel-structure.png"
        alt="LED panel structure"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>
</p>
<p>I thought, that isn’t what Lunar set out to do, simulating brightness that is. But at the same time, a lot of users depend on this app and if it could at least do that, people will be just a bit happier.</p>
<p>So I started researching how the brightness of an image is perceived by the human eye, and read way too much content about the Gamma factor.<br>
Here’s a very good article about the subject: <a href="http://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/"  target="_blank" rel="noopener" >What every coder should know about Gamma</a></p>
<p>I noticed that macOS has a very simple way to control the Gamma parameters so I said <em>why not?</em>. Let&rsquo;s try to implement brightness and contrast approximation using Gamma table:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">minGamma</span> <span class="p">=</span> <span class="mf">0.0</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">gamma</span> <span class="p">=</span> <span class="n">mapNumber</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">brightnessPercent</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">fromLow</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span> <span class="n">fromHigh</span><span class="p">:</span> <span class="mf">1.0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">toLow</span><span class="p">:</span> <span class="mf">0.3</span><span class="p">,</span> <span class="n">toHigh</span><span class="p">:</span> <span class="mf">1.0</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">contrast</span> <span class="p">=</span> <span class="n">mapNumber</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">powf</span><span class="p">(</span><span class="n">contrastPercent</span><span class="p">,</span> <span class="mf">0.3</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">fromLow</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="n">fromHigh</span><span class="p">:</span> <span class="mf">1.0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">toLow</span><span class="p">:</span> <span class="o">-</span><span class="mf">0.2</span><span class="p">,</span> <span class="n">toHigh</span><span class="p">:</span> <span class="mf">0.2</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">CGSetDisplayTransferByFormula</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">displayID</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">minGamma</span><span class="p">,</span> <span class="n">gamma</span><span class="p">,</span> <span class="n">gamma</span> <span class="o">+</span> <span class="n">contrast</span><span class="p">,</span>  <span class="c1">// red gamma</span>
</span></span><span class="line"><span class="cl">    <span class="n">minGamma</span><span class="p">,</span> <span class="n">gamma</span><span class="p">,</span> <span class="n">gamma</span> <span class="o">+</span> <span class="n">contrast</span><span class="p">,</span>  <span class="c1">// green gamma</span>
</span></span><span class="line"><span class="cl">    <span class="n">minGamma</span><span class="p">,</span> <span class="n">gamma</span><span class="p">,</span> <span class="n">gamma</span> <span class="o">+</span> <span class="n">contrast</span>   <span class="c1">// blue gamma</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Of course this needed weeks of refactoring because the app was not designed to support multiple ways of setting brightness <em>(as it usually happens in every single-person hacked up project)</em>.</p>
<p>And there were so many unexpected issues, like, why does it take more than 5 seconds to apply the gamma values?? ლ(╹◡╹ლ)</p>
<p>It seems that the gamma changes become visible only on the next redraw of the screen. And since I was using the builtin display of the MacBook to write the code and the monitor was just for observing brightness changes, it only updated when I became too impatient and moved my cursor to the monitor in anger.</p>
<p>Now how do I force a screen redraw to make the gamma change apply instantly? <em>(and maybe even transition smoothly between brightness values)</em></p>
<p>Just draw something on the screen ¯\_(ツ)_/¯</p>
<p>I chose to draw a (mostly hidden) blinking yellow dot when a gamma transition happens, to force screen redraw.</p>
<hr>
<h3 id="the-raspberry-pi-idea">The Raspberry Pi idea</h3>
<p>Now I was prepared to release a new version of Lunar with the Gamma approximation thing as a fallback for M1. But as it happens, one specific user sends me an email of how he managed to <strong>change the brightness</strong> of his monitor <strong>from a Raspberry Pi connected to the HDMI input</strong>, while the active input was still set to the MacBook’s USB-C.</p>
<p>I have already explored this idea as I have numerous Pis laying around, but I couldn’t get it working at all. I started writing a condescending reply of how I already tried this and how it will never work and he probably just has a monitor that happens to support this and won’t apply for other users.</p>
<p>But then… I realized what I was doing and started pressing <em>backspace backspace backspace…</em> and all the while I was remembering how the best features of Lunar were in fact ideas sent by users and I should stop thinking that I know better.</p>
<p>Instead, I started asking questions:</p>
<ul>
<li>what OS was the Pi running?</li>
<li>was there a specific boot configuration he was using?</li>
<li>what software was he using to send DDC requests?</li>
</ul>
<p>I probably asked the right questions because the reply was exactly what I needed to get this working right away.<br>
After 30 minutes of downloading the latest Raspberry Pi OS with full desktop environment, flashing it, updating to a beta firmware version, and setting the right values in <code>/boot/config.txt</code>, the Pi was able to send DDC requests using <a href="https://github.com/rockowitz/ddcutil"  target="_blank" rel="noopener" >ddcutil</a> while the monitor was rendering the MacBook desktop.</p>
<p>I couldn’t let this slip away, so I started implementing a network based DDC control for the next version of Lunar:</p>
<ul>
<li>a server would run on the Pi and listen for <code>/:monitor/:control/:value</code></li>
<li>the server will advertise itself on the network using mDNS so that Lunar won’t have to scan the whole LAN every x seconds</li>
<li>Lunar will send the brightness, contrast, volume and input values to the server using simple HTTP requests</li>
<li>the server will call <a href="https://github.com/rockowitz/ddcutil"  target="_blank" rel="noopener" >ddcutil</a> with the parameters from the request</li>
</ul>
<p>I established from the start that the local network latency and HTTP overhead was negligible compared to the DDC delay so I didn’t have to look into more complex solutions like USB serial, websockets or MQTT.</p>
<h3 id="the-dreaded-day-job">The dreaded day job</h3>
<p>Even though <strong>side project</strong> is such a praised thing in the software development world, I can’t recommend doing such a thing.</p>
<p>It was very hard doing all of the above in the little time I had after working 9+ hours fullstack at an US company <em>(that was also going through 2 different transitions: bought by a conglomerate, merging with another startup)</em>.</p>
<p>I owe a lot to my manager there, I wouldn’t have had the strength to do what followed without his encouraging advice and always present genuine smile.</p>
<p>One day, he told me that he finally started working on a bugfix for a long-standing problem in our gRPC gateway. He confessed that it was the first time in two months he found the time to write some code <em>(the thing he actually enjoyed)</em>, between all the meetings and video calls. 10 minutes later, another non-US based team needed his help and his coding time got filled with scheduled meetings yet again. That is the life of a technical manager.</p>
<p>Now that Lunar was working on M1 and the <a href="https://www.buymeacoffee.com/alin23"  target="_blank" rel="noopener" >Buy me a Coffee</a> donations showed that people find value in this app, I thought it was time to stop doing what I don’t like <em>(working for companies on products that I never use)</em> and start doing what I always seemed to like <em>(creating software which I also enjoy using, and share it with others)</em>.</p>
<p>So on April 1st I finished my contract at the US company, and started implementing a licensing system in Lunar.</p>
<p>Sounds simple right? Well it’s far from that. Preparing a product for the purpose of selling it, took me two whole months. And more energy than I put in 4 months of experimenting with Gamma and DDC on M1 <em>(yeah, that was the fun part)</em>. This part of the journey is the hardest, and not fun at all.</p>
<p>My take from this is: <strong>if you’re at the start of selling your work, choose a payment or licensing solution that requires the least amount of work, no matter how expensive it may seem at first.</strong></p>
<p>I went with <a href="https://paddle.com/"  target="_blank" rel="noopener" >Paddle</a> for Lunar because of the following reasons:</p>
<ul>
<li>they have a macOS SDK, which meant
<ul>
<li>no need to implement checkout views, license activation dialogs, guarding access to the app etc.</li>
<li>the users can buy the license directly from the app, no need to redirect them to a website</li>
<li>anti-cracking techniques are already better implemented than what I could have done in a few months time</li>
</ul>
</li>
<li>they act as a reseller which means they handle all the taxes
<ul>
<li>I just get a pay-check on the 1st of each month</li>
</ul>
</li>
</ul>
<p>Even with that, I made the mistake to choose a licensing system that wasn’t natively supported by Paddle and that made me dig into a 2-month rabbit hole of licensing servers.</p>
<p><em>I wanted the system that Sketch has: a one-time payment for an unlimited license, that also includes 1 year of free updates.</em></p>














<a href="https://alinpanaitiu.comimages/sketch-licensing.png">
  <figure>
    <figcaption>Sketch app licensing</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sketch-licensing.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sketch-licensing.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sketch-licensing.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sketch-licensing.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sketch-licensing.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sketch-licensing.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sketch-licensing.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sketch-licensing.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sketch-licensing.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sketch-licensing.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sketch-licensing.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sketch-licensing.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sketch-licensing.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sketch-licensing.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sketch-licensing.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sketch-licensing.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sketch-licensing.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sketch-licensing.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sketch-licensing.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sketch-licensing.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sketch-licensing.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sketch-licensing.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sketch-licensing.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sketch-licensing.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sketch-licensing.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sketch-licensing.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/sketch-licensing.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/sketch-licensing.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/sketch-licensing.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/sketch-licensing.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/sketch-licensing.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/sketch-licensing.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/sketch-licensing.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/sketch-licensing.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/sketch-licensing.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/sketch-licensing.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/sketch-licensing.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/sketch-licensing.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/sketch-licensing.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/sketch-licensing.png"
        alt="Sketch app licensing"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<hr>
<h2 id="ic-on-m1">I²C on M1</h2>
<p>After a successful launch in June, most users were happy with the Gamma solution, and some even tried the Raspberry Pi method: <a href="https://talk.macpowerusers.com/t/lunar-app-a-way-for-m1-macs-to-control-3rd-party-monitors-brightness-and-contrast/23677?u=alinp32"  target="_blank" rel="noopener" >Lunar.app - a way for M1 Macs to control 3rd Party Monitor&rsquo;s Brightness and Contrast - Hardware - MPU Talk</a></p>
<p>Although one user was still persistent in looking for I²C support. Twice he tried to bring to my attention a way to use I²C on M1 and the second time he finally succeeded.</p>














<a href="https://alinpanaitiu.comimages/ioavservice-github-comment.png">
  <figure>
    <figcaption>@zhuowei Github comment</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/ioavservice-github-comment.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/ioavservice-github-comment.png"
        alt="@zhuowei Github comment"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>

<ul>
<li><a href="https://github.com/alin23/Lunar/issues/210#issuecomment-871047164"  target="_blank" rel="noopener" >GIthub comment with I²C functions on M1</a></li>
<li><a href="https://twitter.com/zhuowei/status/1374870618894647297?s=21"  target="_blank" rel="noopener" >Twitter reply from 3 months before, that I totally missed because I don’t use Twitter</a></li>
</ul>
<p>His GitHub comment on the M1 issue for Lunar sparked a new hope among users and some of the more technical users started experimenting with the<br>
<code>IOAVServiceReadI2C</code> and <code>IOAVServiceWriteI2C</code> functions.</p>
<p>Because of my shallow understanding of the DDC specification at the time, I couldn&rsquo;t get a working proof of concept in the first few tries.</p>
<p><em>I didn&rsquo;t know exactly what chipAddress and dataAddress were for</em></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">IOAVServiceWriteI2C</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="n">IOAVServiceRef</span> <span class="n">service</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint32_t</span> <span class="n">chipAddress</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint32_t</span> <span class="n">dataAddress</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="kt">void</span><span class="o">*</span> <span class="n">inputBuffer</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="kt">uint32_t</span> <span class="n">inputBufferSize</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>I knew from my experiments with ESP32 and Arduino boards that I²C is in fact a serial <strong>bus</strong>, which means you can communicate with more than one device from the same 2 pins of the main device by chaining the secondary devices.</p>
<p>That possibility brings the requirement of a <strong>chip address</strong> which the main device should send over the wire to reach a specific device from that chain.</p>
<p><em>chaining sensor boards through I²C</em><br>














<a href="https://alinpanaitiu.comimages/i2c-chain.png">
  <figure>
    <figcaption>I²C chain of sensors</figcaption>
    <picture>
      
        
          
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/i2c-chain.png@avif 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/i2c-chain.png@avif 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/i2c-chain.png@avif 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/i2c-chain.png@avif 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/i2c-chain.png@avif 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/i2c-chain.png@avif 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/i2c-chain.png@avif 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/i2c-chain.png@avif 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/i2c-chain.png@avif 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/i2c-chain.png@avif 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/i2c-chain.png@avif 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/i2c-chain.png@avif 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/i2c-chain.png@avif 64w"
              type="image/avif">
          
          
            <source
              sizes="(min-width: 60em) 810px, 90vw"
              srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/i2c-chain.png@webp 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/i2c-chain.png@webp 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/i2c-chain.png@webp 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/i2c-chain.png@webp 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/i2c-chain.png@webp 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/i2c-chain.png@webp 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/i2c-chain.png@webp 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/i2c-chain.png@webp 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/i2c-chain.png@webp 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/i2c-chain.png@webp 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/i2c-chain.png@webp 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/i2c-chain.png@webp 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/i2c-chain.png@webp 64w"
              type="image/webp">
          
          <source
            sizes="(min-width: 60em) 810px, 90vw"
            srcset="https://img.alinpanaitiu.com/_/1920/plain/https://alinpanaitiu.com/images/i2c-chain.png 1920w,https://img.alinpanaitiu.com/_/1620/plain/https://alinpanaitiu.com/images/i2c-chain.png 1620w,https://img.alinpanaitiu.com/_/1400/plain/https://alinpanaitiu.com/images/i2c-chain.png 1400w,https://img.alinpanaitiu.com/_/1280/plain/https://alinpanaitiu.com/images/i2c-chain.png 1280w,https://img.alinpanaitiu.com/_/1200/plain/https://alinpanaitiu.com/images/i2c-chain.png 1200w,https://img.alinpanaitiu.com/_/1080/plain/https://alinpanaitiu.com/images/i2c-chain.png 1080w,https://img.alinpanaitiu.com/_/1024/plain/https://alinpanaitiu.com/images/i2c-chain.png 1024w,https://img.alinpanaitiu.com/_/992/plain/https://alinpanaitiu.com/images/i2c-chain.png 992w,https://img.alinpanaitiu.com/_/768/plain/https://alinpanaitiu.com/images/i2c-chain.png 768w,https://img.alinpanaitiu.com/_/640/plain/https://alinpanaitiu.com/images/i2c-chain.png 640w,https://img.alinpanaitiu.com/_/576/plain/https://alinpanaitiu.com/images/i2c-chain.png 576w,https://img.alinpanaitiu.com/_/320/plain/https://alinpanaitiu.com/images/i2c-chain.png 320w,https://img.alinpanaitiu.com/_/64/plain/https://alinpanaitiu.com/images/i2c-chain.png 64w">
        
      
      <img
        src="https://alinpanaitiu.comimages/i2c-chain.png"
        alt="I²C chain of sensors"
        
        
        style="max-width: 100%"
      >
    </picture>
  </figure>
</a>
</p>
<p>In the DDC standard, the secondary device is the monitor and has the chip address <code>0x37</code>.</p>
<p>The EDID chip is located at the address <code>0x50</code> which is what we have in <a href="https://gist.github.com/zhuowei/bf37b110152aeb61383a3885cb465cc5"  target="_blank" rel="noopener" >@zhuowei&rsquo;s EDID reading example</a></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">IOAVServiceReadI2C</span><span class="p">(</span><span class="n">avService</span><span class="p">,</span> <span class="mh">0x50</span><span class="p">,</span> <span class="mh">0x0</span><span class="p">,</span> <span class="n">i2cBytes</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">i2cBytes</span><span class="p">));</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>But then what is the <code>dataAddress</code>?</p>
<p>No idea, but thankfully someone <a href="http://boichat.ch/nicolas/ddcci/method.html"  target="_blank" rel="noopener" >reverse engineered the communication protocol</a> and found this to always be <code>0x51</code>.</p>
<p>After some trial and error, user <a href="https://github.com/alin23/Lunar/issues/210#issuecomment-872601088"  target="_blank" rel="noopener" >@tao-j</a> discovered the above details and managed to finally change the brightness from his M1 MacBook.</p>
<h4 id="the-mac-mini-problem">The Mac Mini problem</h4>
<p>Unfortunately, this was just the beginning as the Mac Mini supports more than one monitor and it&rsquo;s not clear what monitor we&rsquo;re controlling when calling <code>IOAVServiceCreate()</code>.</p>
<p>I found a way to get each monitor&rsquo;s specific AVService by iterating the I/O Kit registry tree and looking for the <code>AppleCLCD2</code> class. To know which <code>AppleCLCD2</code> belonged to what monitor, I had to cross reference identification data returned by <code>CoreDisplay_DisplayCreateInfoDictionary</code> with the attributes of the registry node.</p>
<p>With that convoluted logic, I managed to get DDC working on Mac Mini as well, but only on the Thunderbolt 3 port. The HDMI port still doesn&rsquo;t work for DDC, <a href="https://github.com/alin23/Lunar/issues/125"  target="_blank" rel="noopener" >and no one knows why</a>.</p>
<p><strong>In the end, DDC on M1 was finally working in the same way it worked on Intel Macs!</strong></p>
<h4 id="sending-ic-data-on-m1">Sending I²C data on M1</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define BRIGHTNESS 0x10
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="n">IOAVServiceRef</span> <span class="n">avService</span> <span class="o">=</span> <span class="n">IOAVServiceCreate</span><span class="p">(</span><span class="n">kCFAllocatorDefault</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">IOReturn</span> <span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">UInt8</span> <span class="n">data</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="n">memset</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">data</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">UInt8</span> <span class="n">brightness</span> <span class="o">=</span> <span class="mi">70</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x84</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">data</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x03</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">data</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">BRIGHTNESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">data</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">brightness</span> <span class="o">&gt;&gt;</span> <span class="mi">8</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">data</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="n">brightness</span> <span class="o">&amp;</span> <span class="mi">255</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">data</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x6E</span> <span class="o">^</span> <span class="mh">0x51</span> <span class="o">^</span> <span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">^</span> <span class="n">data</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">^</span> <span class="n">data</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">^</span> <span class="n">data</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">^</span> <span class="n">data</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">IOAVServiceWriteI2C</span><span class="p">(</span><span class="n">avService</span><span class="p">,</span> <span class="mh">0x37</span><span class="p">,</span> <span class="mh">0x51</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="mi">6</span><span class="p">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Some quirks are still bothering the users of Lunar though:</p>
<ul>
<li>some monitors lose signal or flicker in and out of connecting when the M1 GPU sends any I²C data</li>
<li>the HDMI port of the Mac Mini doesn’t send any kind of data over I²C</li>
<li>reading the currently visible input through DDC never works on M1</li>
<li>reading brightness, contrast or volume from the monitor fails about 30% of the time</li>
</ul>
<p>For the moment these seem to be hardware problems and I’ll just have to keep responding to the early morning support emails no matter how obvious I make it that these are unsolvable.</p>
<h3 id="technical-stuff">Technical stuff</h3>
<p>I left these at the end because the details may bore most people but they might still be useful for a very small number of readers.</p>
<h4 id="how-is-an-app-able-to-change-the-hardware-brightness-of-a-monitor">How is an app able to change the hardware brightness of a monitor?</h4>
<p>All monitors have a powerful <strong>microprocessor</strong> inside that has the purpose of receiving video data over multiple types of connections, and creating images from that data through the incredibly tiny crystals of the panel.</p>
<p>That same microprocessor <strong>dims or brightens</strong> a panel of <strong>LEDs</strong> behind that panel of crystals based on the <strong>Brightness value</strong> that you can change in the <strong>monitor settings</strong> using its <strong>physical buttons</strong>.</p>
<p>Because the devices that connect to the monitor need to know stuff about its capabilities (e.g. resolution, color profile etc), there needs to be a <strong>language known by both the computer and the monitor</strong> so that they can communicate.</p>
<p>That language is called a <strong>communication protocol</strong>. The protocol implemented inside the processors of most monitors is called <strong>Display Data Channel</strong> or <strong>DDC</strong> for short.</p>
<p>To allow for different monitor properties to be read or changed from the host device, VESA created the <a href="https://en.wikipedia.org/wiki/Monitor_Control_Command_Set"  target="_blank" rel="noopener" >Monitor Control Command Set</a> (or <strong>MCCS</strong> for short) which works over DDC.</p>
<p>MCCS is what allows Lunar and other apps to change the monitor brightness, contrast, volume, input etc.</p>
<h4 id="then-what-the-heck-is-ic">Then what the heck is I²C?</h4>
<p>I²C is a Wire protocol, which basically specifies how to translate electrical pulses sent over two wires into bits of information.</p>
<p>DDC specifies which sequences of bits are valid, while I²C specifies how a device like the monitor microprocessor can get those bits through wires inside the HDMI, DisplayPort, USB-C etc. cables.</p>
<h4 id="why-does-macos-block-me-from-changing-volume-on-the-monitor-while-windows-allows-that">Why does macOS block me from changing volume on the monitor, while Windows allows that?</h4>
<p>


<img
    src="https://alinpanaitiu.comimages/volume-lock.png"
    alt="volume lock macOS OSD"
    
    
    
/></p>
<p>macOS doesn’t block volume, it simply doesn&rsquo;t implement any way for you to change the volume of a monitor.</p>
<p>Windows actually only changes the software volume, so if your monitor real volume is at 50%, windows can only lower that in software so you&rsquo;ll hear anything between 0% and 50%. If you check the monitor OSD, you&rsquo;ll see that the volume value of the monitor always stays at 50%.</p>
<p>Now macOS could probably do that as well, so that at least we&rsquo;d have a way to lower the volume. But it doesn&rsquo;t.</p>
<p>So if you want to change the real volume of the monitor on Mac, Lunar can do that.</p>
]]>
      </description>
    </item>
    
  </channel>
</rss>
