cho.sh
2,244 notes
Liked on XI made a mistake. When Jack and I decided to organize this $100K Hackathon, my biggest concern was cheating. Unlike a regular hackathon, everyone participates from home. And because the winner is whoever makes the most PROFIT (not revenue), there are so many ways to manipulate the numbers. So I added a $99 entry fee. One thing I’ve learned from building businesses is that people tend to be more committed when they pay for something. I thought it would filter out spam, attract serious builders, and make the competition fairer for everyone. But I completely missed the other side. 3 years ago, I was a living with my apps. Today, I’m financially free. My life changed so much that somewhere along the way, I lost perspective on what $99 means to someone else. For someone in India, someone struggling to pay their bills, or someone trying to make their first dollar online, $99 can be a lot of money. And those are exactly the people I want to help! I’ve spent years sharing what I’ve learned, reviewing random people’s landing pages, and organizing free meetups around the world. Not because there’s money to make, but because I genuinely love seeing people build things and succeed. And somehow, I was so focused on keeping cheaters out that I didn’t realize I was also keeping genuine builders out. That’s my mistake. So I’ve decided to make the $100K Hackathon completely FREE :) Everyone who already paid got a full refund. I’m still worried about cheating. That problem hasn’t disappeared, and Jack and I will have to figure out better ways to verify profits and keep the leaderboard fair. But that’s our problem to solve. Thank you to everyone who shared their thoughts. Sometimes you need people to remind you of things you’ve forgotten. I genuinely hope this hackathon changes someone’s life. ❤️Liked on XIt’s time to bring back Cafe CursorPosted on Xyo @pendev i can't live share edit a .pen file? it's the most needed featurePosted on XI'm coining a word "Agentsplaining" Agents condescendingly assuming the user don't know fuck and repeating the obviousReplied on X@jayhxmo @raycast Hard to explain. It's the small detailsPosted on X@raycast iOS keyboard is insanely good. Might resub Raycast pro just cuz of thisReplied on X@ishuagra02 I saw a theory that F5.2 is a post train of 5.1, but O5.5 is a distillation of F5.5(unreleased new pretrain) and thats why O5.5 > F5.2Liked on X@anaclumos don't know what is actually improved 👀Posted on Xam i the only one underimpressed with 5.2 fableReplied on X@bvvst great minds think alike https://t.co/YRFWhb1mcOLiked on XClaude Fable 5.2 is out and it's the perfect time to run this god-level prompt on your codebase: "I want you to audit our codebase, is anything here unnecessary, overly complicated in our current setup, or based on weak assumptions? Challenge them. - What can be deleted entirely? - What can be simplified now that unnecessary pieces are gone? Then make the changes. Prefer deleting over simplifying, simplifying over optimizing, and optimizing over automating. This product is being used by thousands of customers, do not cut any corners." This prompt is one I run often and leads to the best results.Replied on X@akshayadinesh19 @bot can we please BYOM for botsLiked on XTo be fair, the Chinese models have a much lower refusal rateLiked on XF*CK IT, im creating a library of AI slop indicators. my feed is full of taste larpers. stay tuned https://t.co/5Muv8nQRm3Liked on XJENSEN! THATS NOT WHAT WE MEANT BY CHINESE MODELS!!!!!!!Liked on Xmy parents had to declare bankruptcy when I was a kid, so they had every reason to pressure me towards a more stable career path instead, they were so patient with me, even as i failed startup after startup for years so getting to tell my mom that everything turned out okay was a really special moment :) for those who can't read Korean, her first message roughly translates to: "Really!!! Congratulations my son. So much :') Thank you God!" i know I've been posting about my exit for like a week now, but it really is a big deal for me and my family! i'll share more about how this all happened and some tactical advice later this week, but for now i'm just riding out all the emotions to quote my mom, thank you God!!!!Liked on X@thefarmers_Dog it stands for Egg GzampleLiked on X🦍 Introducing Memorilla: memory as a modality for LLMs Memorilla turns everything a frozen LLM needs to remember into just 16 memory embeddings ⚡ It uses up to 4,212× fewer tokens and is 27× faster than full context inference while more accurate 🎯 Pushes the Pareto frontier over RAG and agentic memory in personalization, long-document and knowledge QA A thread 🧵:Liked on XThere's a huge product opportunity downstream of Tailscale etc too. At @tldraw we've started designing for "high trust networks" and it's been a beautiful thing. 1. Features that would be a nightmare on the internet are okay on my computer and mostly okay on private networks. Websites that execute user-generated scripts, control production systems, allow for users to send desktop notifications, spend tokens, or directly prompt my agent. Crazy stuff that would only make sense if everyone was in a committed polycule. 2. When everyone on the server can be trusted to have _external_ reasons for good behavior (eg keeping their job, or maintaining friends, family, church, mafia org), the software can be designed in a default social way. Most apps default to isolation, low horizontal visibility, highly restricted collaboration. Makes sense if the social contracts are inside of the app, but if you can trust that they live outside of the app then you can get wild. Example being small group chat names as an emergent comedy phenomenon because anyone in the group can change the name. 3. This can work for commercial products, too, not just internal stuff. Security matters more than ever but the way we've seen that manifest is that more companies are more confident in their own solutions than anything we could provide, to the point where owning data and access is a better value proposition than having it managed. Always has been true for big companies but now even small companies can get a good secure network set up pretty easily, and the hooks we need to build are easy enough. tldraw offline got us started down this road (and shout out to @pendev for first introducing me to the idea of desktop + mcp). We've been bringing that product to private networks and it's been exciting to see how much we can get away with. Keep an eye outReplied on X@saradu birds eye viewLiked on XApplications close in 2 days!! If you have a cool bio idea, and $5-10k will go a long way for you, please apply!!! https://t.co/NCvmrNC53cLiked on XWe've been early adopters of Rust since 2018 and a lot of our infra, voice tech, and even some of the current client is already Rust, the holy grail was getting the whole client there. That means Electron goes away, and so does React Native, mobile keeps native UI but shares app code with desktop via Rust. We hope to get it into testing with users soon™. (And it's not Tauri, it's GPUI, so good guess everyone.) Alright, back to staring at the WoW Forever login screen.Reposted on XOh boy. https://t.co/X7SSskGuGvLiked on XRIP .si domain squatters
Recreating the Dynamic Island

Recreating the Dynamic Island

Warning

Over a year old. May be outdated

🗣 Talk is cheap, show me right now!

Of course. Click on the black oval below. It will display a song I am currently listening to or any of my 30 most recently played songs.

Good artists copy, great artists steal.

And I am now replicating Vercel's DX VP Lee Robinson's idea, LeeRob.io. Known for being a excellent testing bed for new Next.js features, Lee Robinson has one outstanding functionality. It will display the owner's song currently playing.

ALT: leerob.io

Now Playing on Spotify @ leerob.io

and longed to implement this one day on my website. However, I wanted it to be a technical challenge rather than simply recreating it. I also tried out various music services, making me postpone the development. I kept delaying the action until Apple released an exciting feature in 2022 called the Dynamic Island.

ALT: The Dynamic Island

The punch-hole camera at the top will reshape itself into different widgets.

The Dynamic Island perfectly satisfied my desire for a technical hurdle, so I planned to implement it with Web technologies. I also checked out different copies of the Dynamic Island for Android products, which all had awkward animation curves, further getting me interested in learning such details.

💡 Goal

Let's recreate the Dynamic Island on the Web!

💰 Extra Tip

I also open-sourced my for this project.

🛠 Readying the Tech

I went with the most familiar choice for the framework, Next.js and Tailwind. However, the animation troubled me. I have never dealt with anything more complicated than ease-in-ease-out CSS animations. I learned about a production-ready motion library named Framer Motion and opted for it.

ALT: Framer Motion

Framer Motion

🧑🏻‍🏫 The Physics of Animations

We first want to understand why Apple's animations look different from the others. We can classify animations into two big categories. (At least Apple classifies theirs into these two categories in their platform.)

Parametric Curve. Given a start and an endpoint, place a few control points and interpolate the curve in between using mathematical formulas. Depending on the type of interpolation formula, it can be a linear curve, polynomial curve, spline curve, etc. The Bézier curve that many developers often use falls under this category.

Spring Curve. Based on Newtonian dynamics (Hooke's law, the law that governs a spring's physical motion), we calculate the physical trajectory using stiffness and dampening. Learn More: Maxime Heckel

Any further discussions on animation curves will be out of the scope of this post. Most replications of the Dynamic Island choose parametric curves (it's the easiest, standardized in CSS). Apple uses spring motion, supposedly to mimic real-world physics. Framer Motion, the library I chose for this project, also provides a React hook named useSpring() to give control of such physical animations.

ts
useSpring(x, { stiffness: 1000, damping: 10 });

🛥 To the Dynamic Island

Source: Apple

I had to study the different behaviors of the Dynamic Island with Apple's official documents. The Dynamic Island can be any of the following forms.

Minimal. The widget takes one side of the Dynamic Island when two or more background activities are ongoing.

Compact: The standard form, where the widget takes both sides of the Dynamic Island when there is one ongoing background activity.

Expanded: The biggest size of the the Dynamic Island, shown when the user long-presses on the Dynamic Island. It cannot display content in the red area.

I also found the following image on the Web. Apple puts Expanded for all big sizes, but this image describes the Dynamic Island's expanded states.

Different sizes of the Dynamic Island. Considering that there is a typo on the image, it doesn't seem like an official document.

I declared the type as follows, reflecting the earlier information.

ts
export type DynamicIslandSize =
  | "compact"
  | "minimalLeading"
  | "minimalTrailing"
  | "default"
  | "long"
  | "large"
  | "ultra";

Then I spent a whole night () and figured out how to naturally shift sizes with Framer Motion. It uses the following code. I especially experimented with a lot of stiffness and dampening values. The golden ratio was const stiffness = 400 and const damping = 30.

tsx
<motion.div
  id={props.id}
  initial={{
	opacity: props.size === props.before ? 1 : 0,
	scale: props.size === props.before ? 1 : 0.9,
  }}
  animate={{
	opacity: props.size === props.before ? 0 : 1,
	scale: props.size === props.before ? 0.9 : 1,
	transition: { type: 'spring', stiffness: stiffness, damping: damping },
  }}
  exit={{ opacity: 0, filter: 'blur(10px)', scale: 0 }}
  style={{ willChange }}
  className={props.className}
>

ALT: As of Oct 16th, 2022

As of Oct 16th, 2022

📞 Hello?

Before connecting with external APIs, I mimicked Apple's incoming phone call widget. There's no big reason for this. It was just to get used to the animations. I love how it turned out. It looks exactly like the official Apple animation! Finished on .

🍎 Apple Music API

Then I needed to integrate with Apple Music's API. I previously made a technical demo with Spotify's API at the beginning of 2021. Spotify officially has a Now Playing API, so naturally, I expected a similar Now Playing API at Apple Music.

I was a huuuuge fan of IZ*ONE back then... 😅

ALT: Spotify Now Playing API

Spotify for Developers

Apple Music API 1.1 added Get Recently Played Tracks, the closest thing to a Now Playing API we ever got. Two years ago even that didn't exist.

ALT: Apple Music Get Recently Played Tracks

FYI, such an API did not even exist two years ago.

Now we need to issue and save the different tokens needed for OAuth 2.0. Spotify almost precisely followed the OAuth 2.0 standards, while Apple required a little more processing. The Now Playing API, especially, accessed the data on the Apple Music server and the user's private data, so I needed a separate privilege control with user access grants. All of these needed to be better documented, making it significantly more complicated. I needed the following items.

Aviation IndustrySame Concept at AppleExplanation
Establishing your Aviation CompanyApple Developer Paid Account$99
Pilot LicenseApple Music Key from Apple Developer Website.Ensures that you have permissions to get data from Apple Servers
Air Carrier Operating PermitApple Music Main Token from requesting to Apple ServerA permit to attach when requesting to Apple
Airline tickets for PassengersApple Music User Token from user's grantEnsures if the user wants to use my service

All four pieces of information should work harmoniously to retrieve users' data (of what they were listening to). All the others were pretty straightforward (). The trickiest one was User Token. User Token was specialized for iOS, macOS, and MusicKit on the Web. MusicKit on the Web was intended for Apple Music Web clients, like music.apple.com, Cider, and LitoMusic and was not designed for such API request bots. Still, Apple put MusicKit on the Web will automatically take care of it without documenting it. So what are we going to do? Reverse engineer the API.

Apple: Documentation? Nah.

ALT: MusicKit on the Web

MusicKit on the Web. Is Apple using Storybook? Based on Apple's track record, this MUST be an Alpha of Alpha.

🦾 Cracking the MusicKit

First, I mimicked the specs of MusicKit on the Web, creating a website.

This website will do nothing else than calling the authorization process.

It will show the Apple Music access grant page like this.

Then digging into the request headers of the website will reveal the media-user-token.

There we go.

Finally, I can successfully get a JSON response from the Apple server by filling in other information with Postman software. Finished on .

It sounds straightforward, but it took me days to figure it out. 😭

Requiring the information whenever someone accesses the Web will deplete my API quota in minutes. I wanted to make a cache server of some sort. But remember, the best database is no database.

Don't use the database when avoidable. Which is always more often than I think. I don't need to store the 195 countries of the world in a database and join when showing a country-dropdown. Just hardcode it or put in config read on boot. Hell, maybe your entire product catalogue of the e-commerce site can be a single YAML read on boot? This goes for many more objects than I often think. It's not Ruby that's slow, it's your database

So I put my private keys in GitHub Secrets and had GitHub Actions pull the data every few minutes and publish it on GitHub.

I don't know how long I struggled to find this typo.

🎼 Equalizers

Similar to finishing the phone call component, I completed the music player component.

But something felt empty.

There were no equalizers! I searched for good equalizers for React but later decided to implement this with Framer Motion. So these are the few iterations of the product.

ALT: FANCY by TWICE ALT: After Like by IVE ALT: Hype Boy by NewJeans

Each stick of the equalizers will have a random length. But as seen in the last song, something was also awkward. Usually, vocal music has smaller amplitudes on low and high frequencies, but completely randomizing the amplitude will also make those frequencies have similar ups and downs. So I set a base length as an anchor and made the randomized values slightly shake the values. Finally, I set the equalizer color to match the album cover's key color. I did not need additional work. It came with the Apple Music API.

Much smoother.

🔎 The Physics of Squircles

We're not done yet! Such completed widgets still felt slightly off. The curves felt too sharp. We needed squircles.

ALT: Squircle

Source: Apple's Icons Have That Shape for a Very Good Reason @ HackerNoon

A standard curve made by setting a border-radius has constant curvature, leading the end of the curve to have a sudden surge in curvature, making it feel sharp. On the contrary, gradually increasing and decreasing the curvature will make a much more natural curve.

For those AP Physics nerds, it's like uniformly increasing the jerk instead of uniformly accelerating.

For those AP Calc nerds, a squircle is a superellipse, the points that satisfy this equation. nn is the curvature, aa is the length on the xx axis, and bb is the length on the yy axis. For a deeper dive, see Figma's article on squircles.

∣xa∣n+∣yb∣n=1{\lvert{x \over a}\rvert}^n + {\lvert{y \over b}\rvert}^n = 1

I used tienphaw/figma-squircle to make an SVG squircle and clip the Dynamic Island with clipPath.

I saw a similar bug at the iOS 16 Notification Center. Maybe Apple is also clipping?

However, to clip all frames of the animations, we would have to create squircles for every frame, risking speed. Therefore, I opted to use borderRadius for the animation and clipped it right after the animation finished. It was barely noticeable, even if you looked very closely, so it was a good trade-off between performance and detail. Finished on .

Look closely. The border cuts into the squircle when the animation exits.

💨 Optimizing Performance

CSS has a will-change property. It tells the browser which elements on the screen will change, preparing the browser for it beforehand. The browser rasterizes every frame if there is no will-change property. With it, the browser will reuse a static image while the animation processes, rasterizing only when the animation finishes. Therefore, the animation may seem blurry depending on the type, but it will give more fluidity for transform, scale, or rotate animations.

The Dynamic Island usually modifies scale and opacity, so it was perfect for will-change. We can apply the property in Framer Motion, as in the following example.

ts
// ...

const willChange = useWillChange();

// ...

🔗 Integration

Last but not least, I made pages for integration purposes (/embed-player, /embed-phone-call). I did not want to add Tailwind or Framer Motions as a dependency on other websites, so I tried to use the iframe method. I used davidjbradshaw/iframe-resizer to make a responsive iframe. I also used CSS's position: sticky property to make it stick on specific pages. It's on this website, too!

💭 Postmortem

This completes the project. Here are some thoughts.

First of all, I succeeded in managing a mid-to-long-term side project. I have always , and I was very happy to finally complete the project after working on it for more than a month. I was also delighted that I successfully juggled 🤹 CS Major classes, job searching, and side projects simultaneously (although they still need to be completed).

Second, I would like to express my gratitude to Tim (cometkim), whom I met during my previous internship. I had a memorable experience during this internship when Tim showed me that it is possible to reverse engineer a compiled webpack codebase. It was indeed a spiced-up 🌶 and intense learning environment. However, that gave me confidence when I was blocked by Apple's undocumented API services.

I am also developing the habit of note-taking. There's a saying that people's will is weaker than we think, so it's better to reshape the environment. I did a decent job remodeling my website as a digital garden (or ) that is optimized for note-taking. I want to continue taking notes and learning new stuff. Tim also had a significant effect my note-taking by showing his workspace on Roam Research.

Anyway, that's the project. Thank you, everyone!

Backlinks2