<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="pretty-atom-feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Matt Hamilton&#39;s blog</title>
  <subtitle>Writing about stuff I&#39;ve built, am working on, or am interested in.</subtitle>
  <link href="https://blog.eartothenoise.com/feed/feed.xml" rel="self" />
  <link href="https://blog.eartothenoise.com/" />
  <updated>2025-10-16T00:00:00Z</updated>
  <id>https://blog.eartothenoise.com/</id>
  <author>
    <name>Matt Hamilton</name>
  </author>
  <entry>
    <title>Adding an Analytics Dashboard for Toy Boat (in an hour)</title>
    <link href="https://blog.eartothenoise.com/toy-boat-analytics/" />
    <updated>2025-10-16T00:00:00Z</updated>
    <id>https://blog.eartothenoise.com/toy-boat-analytics/</id>
    <content type="html">&lt;p&gt;A few weeks ago I wanted better visibility into how families are using &lt;a href=&quot;https://toyboat.co&quot;&gt;&lt;strong&gt;Toy Boat&lt;/strong&gt;&lt;/a&gt; — the app for families to play together from anywhere. Rather than starting from scratch, I tried &lt;a href=&quot;https://lovable.dev/&quot;&gt;&lt;strong&gt;Lovable&lt;/strong&gt;&lt;/a&gt; and ended up building a working analytics dashboard in about an hour during one of &lt;a href=&quot;https://buildfirst.ai/events&quot;&gt;Bethany Crystal&#39;s Build First AI workshops&lt;/a&gt;. I had some extra Lovable credits, so why not?&lt;/p&gt;
&lt;p&gt;You can see the dashboard live here: &lt;a href=&quot;https://toy-boat-analytics.lovable.app&quot;&gt;toy-boat-analytics.lovable.app&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://blog.eartothenoise.com/toy-boat-analytics/5MMFl5kdcA-3208.avif 3208w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://blog.eartothenoise.com/toy-boat-analytics/5MMFl5kdcA-3208.webp 3208w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://blog.eartothenoise.com/toy-boat-analytics/5MMFl5kdcA-3208.png&quot; alt=&quot;&quot; width=&quot;3208&quot; height=&quot;1802&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-setup&quot;&gt;The Setup&lt;/h2&gt;
&lt;p&gt;I kept the stack intentionally simple and close to the product:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Uses the same login as &lt;a href=&quot;https://toyboat.co&quot;&gt;Toy Boat&lt;/a&gt;. If you can sign into the app, you can sign into the dashboard.&lt;/li&gt;
&lt;li&gt;Server-side check verifies you&#39;re an admin before granting access.&lt;/li&gt;
&lt;li&gt;All data comes directly from Supabase — no separate analytics database to maintain.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This meant I could ship something useful fast, without introducing new systems that would drift out of sync.&lt;/p&gt;
&lt;h2 id=&quot;what-i-built-during-the-workshop&quot;&gt;What I Built During the Workshop&lt;/h2&gt;
&lt;p&gt;In roughly an hour, I had Lovable scaffold the dashboard, wire up auth, and render the first charts: total users, daily chat activity, new signups, user distribution, and recent activity. The starter UI was clean enough that I focused most of my time on queries and getting the numbers right.&lt;/p&gt;
&lt;h2 id=&quot;a-little-more-polish&quot;&gt;A Little More Polish&lt;/h2&gt;
&lt;p&gt;After the workshop, I spent a couple more hours tightening things up and adding a &lt;a href=&quot;https://toy-boat-analytics.lovable.app/demo&quot;&gt;&lt;strong&gt;demo mode&lt;/strong&gt;&lt;/a&gt; so I could share the dashboard publicly without exposing private data. Demo mode swaps the data layer for realistic-but-fake numbers while keeping the real UI and interactions.&lt;/p&gt;
&lt;h2 id=&quot;why-this-approach-works&quot;&gt;Why This Approach Works&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Single source of truth: queries read from Supabase directly; no ETL.&lt;/li&gt;
&lt;li&gt;Familiar login flow: no new accounts for the team to manage.&lt;/li&gt;
&lt;li&gt;Fast iteration: Lovable handled most of the scaffolding so I could spend time on metrics.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Big thanks to &lt;a href=&quot;https://buildfirst.ai/events&quot;&gt;Build First&lt;/a&gt; for the push to just ship it!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>I Built an AI Stretching Coach for a Kids&#39; Speaker (Yoto)</title>
    <link href="https://blog.eartothenoise.com/daily-stretch/" />
    <updated>2025-10-10T00:00:00Z</updated>
    <id>https://blog.eartothenoise.com/daily-stretch/</id>
    <content type="html">&lt;p&gt;Last month &lt;a href=&quot;https://yoto.space/news/post/build-a-yoto-app-and-you-could-win-up-to-5-000-hQVNmKqCsfLNoj0&quot;&gt;Yoto announced a developer challenge&lt;/a&gt; for their screen-free audio player, and I thought &amp;quot;why not?&amp;quot;&lt;/p&gt;
&lt;p&gt;So I built &lt;a href=&quot;https://share.yoto.co/s/4HiAP3hhZ0gKb0wrmsPAp3&quot;&gt;Daily Stretch&lt;/a&gt; - an app that generates a fresh stretching routine every single day. No screens, just audio guiding you through 5-10 minutes of movement.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://blog.eartothenoise.com/daily-stretch/pMqVC0w3X_-638.avif 638w&quot;&gt;&lt;source srcset=&quot;https://blog.eartothenoise.com/daily-stretch/pMqVC0w3X_-638.heif 638w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://blog.eartothenoise.com/daily-stretch/pMqVC0w3X_-638.webp&quot; alt=&quot;&quot; width=&quot;638&quot; height=&quot;1011&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-setup&quot;&gt;The Setup&lt;/h2&gt;
&lt;p&gt;The tech stack is pretty straightforward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Claude writes the daily stretching scripts&lt;/li&gt;
&lt;li&gt;Each script is for a specific day, using a monthly theme (for example, October is fall + Halloween + fun spooky)&lt;/li&gt;
&lt;li&gt;Gemini Text-to-Speech turns them into audio&lt;/li&gt;
&lt;li&gt;API endpoint just points to the right file (deployed to Google Cloud Run)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-tricky-part&quot;&gt;The Tricky Part&lt;/h2&gt;
&lt;p&gt;Getting the AI to generate good stretching instructions was harder than I expected. The first few attempts were... not great. Claude would rapid-fire instructions without giving you time to actually DO the stretch (and it still does this on occasion).&lt;/p&gt;
&lt;p&gt;It took a few iterations to get the prompt right. I had to be really specific about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Building in pauses between instructions (you need time to get into position!)&lt;/li&gt;
&lt;li&gt;Timing each hold properly&lt;/li&gt;
&lt;li&gt;Smooth transitions between stretches&lt;/li&gt;
&lt;li&gt;Keeping an encouraging, calm tone throughout&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Turns out there&#39;s a big difference between &amp;quot;generating a list of stretches&amp;quot; and &amp;quot;creating an audio experience that feels natural.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://blog.eartothenoise.com/daily-stretch/8ZisAnsNq2-1086.avif 1086w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://blog.eartothenoise.com/daily-stretch/8ZisAnsNq2-1086.webp 1086w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://blog.eartothenoise.com/daily-stretch/8ZisAnsNq2-1086.png&quot; alt=&quot;&quot; width=&quot;1086&quot; height=&quot;1056&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What&#39;s Next&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://share.yoto.co/s/4HiAP3hhZ0gKb0wrmsPAp3&quot;&gt;Daily Stretch&lt;/a&gt; is live! I&#39;m curious whether people will actually use it daily or if the novelty wears off after a week or two.&lt;/p&gt;
&lt;p&gt;If you have a Yoto player and want to try it, I&#39;d love your feedback - especially if you use it with kids. Does it hold their attention? Is the pacing right?&lt;/p&gt;
&lt;p&gt;I&#39;m also thinking about this pattern more broadly - AI-generated daily content that feels natural and human. Could work for other types of audio experiences too.&lt;/p&gt;
</content>
  </entry>
</feed>