130 Widgets

Semi-random thoughts and tales of tinkering

Building a Spectrum Analyzer for iPhone - A Tutorial

Fifteen years ago I built iPhone apps for a short while. Objective-C, UIKit, Xcode 3. Then, I got a new job and ended up never giving myself permission to devote any time to the Apple plaform.

Long before that, I had brief hopes of getting into music software engineering. That story is for another day, but for at least two years I’ve wanted to write my own tuner app for guitars and violins. I thought this would be reasonable project and a good excuse to learn Swift. But, I’ve never made time.

Now, we have powerful creation tools available to scratch itches. Time is less of a limiting factor. But, in keeping with a new theme, I also care deeping about these creation tools being powerful educators, and not just producers.

The result is a small Swift/SwiftUI app – and, most importantly, tutorial – that does real-time FFT analysis and displays 48 animated frequency bars with musical note detection. Four files, about 200 lines of code. It runs on an iPhone and it feels like magic when you sing into it and watch the bars dance. There will also be a Mac verison, because I need to tune guitars at my desk, too. And, in the near future, I hope my wife and kids will be able to use it on their amazing journeys with the violin.

The tutorial

I wrote up the whole process as an 11-section tutorial aimed at programmers like me — people who know how to code but have never touched Swift, SwiftUI, or digital signal processing.

It runs on two parallel tracks: iOS development and audio/DSP theory. Each theory section arrives right before the code that implements it, so you’re never reading about something abstract — you’re about to use it.

The sections:

  1. Preface & Orientation - What you’ll build, C# to Swift quick reference
  2. Your First iOS App - Xcode project, SwiftUI fundamentals
  3. What Is Sound? - Sampling, PCM, Nyquist, RMS, decibels
  4. Capturing Audio on iOS - AVAudioEngine, mic permissions, a working VU meter
  5. Accelerating the Math - Apple’s Accelerate/vDSP framework
  6. The Fourier Transform - FFT intuition, frequency bins, window functions
  7. Building the Spectrum Analyzer - The SpectrumAnalyzer struct, log-spaced bars, note detection
  8. The Spectrum UI - SpectrumView component, dark theme, final ContentView
  9. Running on Your iPhone - Device setup, provisioning, debugging audio
  10. Beyond the Tutorial - TestFlight, App Store, better pitch detection
  11. Complete Source Code - All four final files with cross-references

By the end of Section 3 you have a working VU meter. By the end of Section 7 you have the full spectrum analyzer. The last three sections cover getting it onto a real device and where to go next.

How it was built

Like the 2D Minecraft tutorial, this was built with AI coding tools — this one was specifically with Claude Code. The app itself came out of a conversation where I was learning, asking questions, and iterating. The tutorial came afterward, distilling that learning journey into something structured.

The interesting thing about using AI for this wasn’t the code generation, and it wasn’t just the conversation. It’s the opportunity to capture the conversation as a design for sharing the knowledge. And, for me, for retaining the knowledge and expertise. If you don’t know how the tools need to build something, you’re going to be a terrible shepherd in the future.

I’m still not sure whether I’m “back” to iOS development or just visiting. But I learned more in a weekend than I expected to, and I have a working app that makes sound visible. That’s a good start.

Start the Tutorial

Build Your Own 2D Minecraft - A Game and Tutorial

I built a 2D Minecraft clone that runs entirely in the browser - no downloads, no installs, just a single HTML file with JavaScript and canvas rendering.

And, when I say “I built”, I mean “I used AI coding tools to build”. But the point was not to build a 2D Minecraft clone. The point was to build a set of interesting coding exercises, and to build it quickly. These coding tools are not just an opportunity to generate product; they’re an opportunity to learn, if you just take the time to ask.

In my opinion, “What” is interesting and often amazing, but is not as interesting as “How” or “Why”.

⛏️ Play the Game

This game as procedurally generated terrain, mining, building, an inventory system, crafting, a day/night cycle, and more. All in about 800 lines of vanilla JavaScript.

The Tutorial

More fun than the game itself: I generated a 12-lesson tutorial that walks through building it from scratch, aimed at a fifth-grade reading level. Each lesson is a self-contained web page with live interactive demos embedded right in the page.

The lessons build progressively:

  1. Hello, Canvas! - HTML basics and drawing rectangles
  2. Colors & Shapes - Variables, functions, building a character
  3. The Animation Loop - The game loop and requestAnimationFrame
  4. Keyboard Controls - Event listeners and WASD movement
  5. Gravity & Jumping - Velocity, acceleration, physics
  6. A World of Blocks - 2D arrays and grid rendering
  7. Collision Detection - Player vs. block collision
  8. Scrolling Camera - Worlds bigger than the screen
  9. Breaking & Placing Blocks - Mouse input and world modification
  10. Inventory System - Collecting items and hotbar management
  11. World Generation - Noise functions and procedural terrain
  12. The Complete Game - Day/night, crafting, health, and polish

Every concept is explained with analogies a kid would understand (flip-books for animation, lockers for arrays, light switches for key tracking), and each lesson ends with challenges to encourage experimentation.

📚 Start the Tutorial

iPhone 14 Pro Max

This is my year for an iPhone update, and I received the new phone yesterday. Upgrading from my iPhone 12 Pro Max was more straightforward than I feared, and definitely more seamless than last time. I used Quick Start to transfer, and things worked as expected… mostly.

My service is with Verizon, and the new iPhone was purchased without any carrier affiliation. I was using the standard old-fashined SIM in the iPhone 12 Pro Max.

  1. Right before starting the process, I did a fresh iCloud back-up.
  2. I started up the new phone and selected Quick Start.
  3. I chose the direct transfer between devices. This did not take nearly as long as the warned estimation time.
  4. I followed the prompts and most things transferred seamlessly. I did not select carrier activation during this parocess.
  5. It provided me the option of transferring over my Apple Watch connection. I left the selection as-is. I did not unpair from the old phone or anything unintuitive as was required in the past.
  6. I use Microsoft Authenticator. My accounts did not transfer, but I realized I was not using iCloud back-up for Authenticator. I turned this on in the app on the old phone, and got the accounts to show up on the new one. However, for most of the identities I had registered, I had to re-estabilish the device to MFA for them. For Microsoft identities, this involved visiting https://mysignins.microsoft.com/security-info for each and generating the QR code dance.
  7. I also had to re-establish corporate management of my device, which involves Microsoft Intune and Defender installation. Attempting to re-enter the password to corporate email account is one way to make sure this is all getting kicked off.
  8. I had to restablish my Testflights by launching the Testflight app and re-installing. They did not find their way back to their locations in Springboard.
  9. Developer profile policies had to re-downloaded and re-trusted.
  10. Finally, I wanted to transfer the number to the new phone, which involves eSIM, now. This involved going to the Verizon portal and going through their “activate your own phone on an existing number” flow. In Verizon’s the instructions it led me to enter the id of the new phone’s second IMEI (which is apparently the second SIM identifier - “IMEI2” - in the Settings -> General -> About). Note, their documentation says the first IMEI is for an inserted SIM card. iPhone 14 Pro only has eSIM, but it shows IMEI and IMEI2; I provided IMEI2 to Verizon and got a green checkbox.
  11. I confirmed the transfer on the Verizon site, and it sat for a minute and eventually failed with an error saying to call their support line. It had at least half-way worked, however, as my old phone no longer got cellular singal. However, the new phone didn’t work, so I started the support call on another phone (oops).
  12. While waiting, I poked on the new phone, and eventually saw a top-level prompt in Settings that there was an activating step waiting. I noticed this as an operator was finally coming on the line; I pressed the confirm button in Settings, and the phone went live almost immediately. The support person had me perform a test call, but all was fine.

When reading this it may sound like a lot, but this entire process took probably 2 hours.

Apple Studio Display

For my birthday this year, I was treated to a new Apple Studio Display. I’ve always been envious of Apple-branded monitors and have wanted one at least since I first saw the 30” Cinema HD display. I got the height-adjustable stand option and I don’t regret it a bit; the movement is very smooth, and the craftsmanship of the piece is great.

One thing of mention, however, is my experience with the built-in webcam. It has received a lot of bad press, with its image quality being mediocre, at best, especially in low-light situations. I should note that this is not my work webcam (which I use constantly through each day). I primary use it for calls to family, and often when my kids will come into and out of the call. For this reason, the Center Stage feature of the camera works very well, dynamically expanding and contracting the shot as people come in and out of range.

I’ve used it quite a bit, and I’ve found the picture quality acceptable, the audio quality much better than expected, and the overall experience very nice. I’ll always wish for better video quality, but I’m very happy with the camera’s currently level of performance - enough so that I’ve exclusively used the Mac for FaceTiming over the iPad Pro I have positioned immediately to the right of the screen.

Keyboard play, part 2

First, I tried the Keychron Q1. I got one with a knob, which I like to use to control volume on my Mac, and which I dislike since the knob is in a key-shaped hole in the top case.

When I am programming (especially on Windows), I find I’m a heavy user of Home/End keys, and I find the traditional tenkeyless 85% keyboard more comfortable. Enter the Keychron Q3. No knob on this one.

I’m eager to experiment with a split keyboard for the first time in many years, now that Keychron has an Alice layout keyboard with function keys. It also has a knob with a proper circular case hole.

My switches sweet spot is Gazzew Boba U4T switches. I prefer lighter springs. I also love tall key caps, and find the Drop MT3 keycaps to be the perfect shape. They come in ABS and in PBT, but I vastly prefer rougher feel of the PBT. The 9009 and the 2048 are on my Q3 and Q1, respectively.

Keyboard play, part 1

I’ve always had a love of tactile keyboards. For the past six or seven years I have loved using a WASD Code 87-key keyboard with Cherry MX Brown switches. Despite also having an example of the same keyboard with Cherry Clear switches, I clearly prefer the lighter spring weight of the Cherry Browns. This was enough for me, until two years ago when I was able to order an aluminum outer case for the same keyboard. I replaced the ABS plastic case with the much-heavier aluminum, and immediately preferred the added heft. Even though my beloved Model M keyboards were made of plastic, I always preferred their heft. They don’t move around on the desk, and they just feel solid.

Building and customizing keyboards has become quite a popular hobby in the past few years, and it seemed to me to be mostly focusing on unique key caps and lighting. This didn’t appeal to me, because half of my love of the Code is, while their keys are backlit, it is an extremely clean, utilitarian design.

Enter hot-swappable (no soldering required) switches. With the right keyboard, I can now tinker with how the switches themselves feel and sound, without the trouble of soldering.

This will be fun.

KQL primer, simplified

A good official tutorial exists

As prep for a learning presentation at work on the topic of KQL, I set out to turn a quick set of “primer” notes into a more thought-out blog post, and then hopefully a better presentation. Turns out, I quickly found a far better introduction to the language. This docs.microsoft.com tutorial is wonderful; it is simple and straightforward, and I used it for my talk.

Learn how to dig

So, rather than specifically a technical introduction, what is it that I am trying to share about KQL? Kusto is such an amazingly performant system, that using it can actually be fun.

What I want to convey in 15 minutes or less is how to discover what data you have, and how to conveniently dig. I have met a lot of engineers whose systems publish to a KQL system. They’ve had access to the data for years, and don’t know how to discover what’s there.

The noise of that sea of poorly conceived and messy telemetry can be intimidating. Don’t let it be.

So, my old KQL primer has become the following “things to learn”. When I share these, I always hope it sparks a little curiosity. That’s all.

Learn the following concepts, as quickly as possible.

  1. Browse tables, looking at a few random records.

    traces | take 10
    

    Perhaps union all your tables together:

    union traces, exceptions, ... |
    
  2. Filter on timestamps. Most KQL UIs will allow you to paste the text of a timestamp and it will wrap it in a datetime() declaration.

     | where timestamp > ago(1d)
     | where timestamp > [PASTE TIME] and timestamp < [PASTE TIME]```
     | where timestamp between (datetime(2022-01-12 19:54:00) .. datetime(2022-01-12 20:10:54)))
    
  3. Search for strings. Get hooked on Kusto’s amazing indexing.

     // no substring matches
     StormEvents
     | where * has "caro"
    
     // includes substring matches
     StormEvents
     | where * contains "caro"
    
  4. Create your own columns, on the fly. Compute new values, or pull data from a column with a complex data object out to its own column.

     StormEvents
     | extend Duration = EndTime - StartTime
    
     Covid19_Bing
     | where Id == "/US/North Carolina"
     | extend ConfirmedValue = Confirmed.Value,
         ActiveValue = Active.Value,
         DeathsValue = Deaths.Value,
         RecoveredValue = Recovered.Value
    
  5. Remove columns you don’t need.

     | project Id, ReportDate, ConfirmedValue, ActiveValue, DeathsValue, RecoveredValue
    
  6. Group results and run functions over the set:

     Covid19_Bing
     | where Id == "/US/North Carolina"
     | extend ConfirmedValue = Confirmed.Value, ActiveValue = toint(Active.Value),
         DeathsValue = Deaths.Value, RecoveredValue = Recovered.Value
     | project Id, ReportDate, ConfirmedValue, ActiveValue,
         DeathsValue, RecoveredValue
     | summarize count(), toint(avg(ActiveValue)) by bin(ReportDate, 31d)
    

Note, I’m assuming most queries will be using the sample data from the tutorial listed above, but my one criticism of the data from that tutorial is that it is not typical system telemetry. I’ll give examples for the following when I find a good sample dataset to use.

  1. Get values out of JSON columns.

  2. Join against other records.

Jekyll is fun

So, with some free holiday hours, I’ve found playing with Jekyll to be more fun than expected. I guess I shouldn’t be surprised.

I wanted to do one of two things: Either start competely from scratch with a bare-bones GitHub Pages blog repo, or fork from Barry Clark’s jekyll-now repo. I tried both.

With the bare-bones repo, with no prior knowledge of Jekyll at all, it was very straightforward how to get a page to be served, but not how to create a blog of many files, all in a time series.

While the forked repo strategy was extremely simple to get started, I did not find it to be tinker-friendly without any knowledge of Jekyll. I can’t keep away from tinkering at least a little bit with a theme.

Eventually, it became apparent to me I should just learn what Jekyll is doing, and it turns out there is a wonderful Step by Step tutorial on the Jekyll site. There are ten steps, but they are pretty simple, and they each explain the logic of the components of Jekyll. I followed the tutorial on Windows, and pretty much ended up with a complete (albeit unstyled) site, running locally. Grab some existing CSS from a few blogs, and a tiny bit of tinkering later I have something a little closer to what I want.

Hello world again

In spare moments I’ve been looking for a quick blogging solution, and now I have one.

GitHub Pages is a wonderful free resource for all users of GitHub, both free and paid. Plus, it’s extremely easy to get started with it. You can start from scratch, or fork a repo to get a headstart.

  1. Get a GitHub account, if you don’t already have one.
  2. Fork Barry Clark’s jekyll-now repo.
  3. Rename your fork’s repository to <yourgithubname>.github.io.
  4. Done!

What you end up with is a static site. This basically means there is no database needed to run things. GitHub provides Jekyll (the code that generates the static pages) as a service. All of my content are Markdown text files, which are versioned with Git.

If you are interested in the same, please check out Barry Clark’s extremely helpful repo and explanations:

What’s next?

What’s long-term?