<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mr.x</title>
    <description>The latest articles on DEV Community by Mr.x (@mrzhangguoguo).</description>
    <link>https://dev.to/mrzhangguoguo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3762273%2F582a93df-7c7e-4ee7-81f4-6054e8915e9b.jpg</url>
      <title>DEV Community: Mr.x</title>
      <link>https://dev.to/mrzhangguoguo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mrzhangguoguo"/>
    <language>en</language>
    <item>
      <title>Shaving Latency Off Real-Time Speech Translation: What Actually Worked in Our Flutter App</title>
      <dc:creator>Mr.x</dc:creator>
      <pubDate>Fri, 09 Oct 2026 04:39:30 +0000</pubDate>
      <link>https://dev.to/mrzhangguoguo/shaving-latency-off-real-time-speech-translation-what-actually-worked-in-our-flutter-app-2kff</link>
      <guid>https://dev.to/mrzhangguoguo/shaving-latency-off-real-time-speech-translation-what-actually-worked-in-our-flutter-app-2kff</guid>
      <description>&lt;p&gt;In a chat app, a slow reply is mildly annoying. In a spoken conversation, it breaks the exchange. Silence feels longer than a spinner does, and people judge the delay from the moment they &lt;em&gt;stop&lt;/em&gt; speaking, not from when a request reaches a server.&lt;/p&gt;

&lt;p&gt;We build &lt;a href="https://translator.owll.ai" rel="noopener noreferrer"&gt;Owll Translator&lt;/a&gt;, a real-time interpretation app. You wear earbuds, speak normally, and the app translates live. It can also speak the translation in a clone of your own voice. Most of our latency work over the past months hasn't been about making any single model faster. It has been about &lt;strong&gt;perceived latency&lt;/strong&gt;: hiding the unavoidable waits, cutting the avoidable ones, and getting sound into the listener's ear as early as we responsibly can.&lt;/p&gt;

&lt;p&gt;This post covers what worked, what didn't, and what we still don't know. One caveat before we start: we don't have trustworthy end-to-end mic-to-ear numbers yet, so there are no millisecond claims here. The last section explains why and what we're doing about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline
&lt;/h2&gt;

&lt;p&gt;We run more than one backend path, and the server picks one per session. Here is a simplified view of both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; (a) Client-orchestrated path
 ----------------------------
 Phone mic
   -&amp;gt; native recorder (PCM 16 kHz, 16-bit, mono)
   -&amp;gt; local VAD (prob threshold 0.5) + noise suppression
   -&amp;gt; cloud speech SDK: continuous recognition + translation
        (push stream; "recognizing" partials, "recognized" finals)
   -&amp;gt; client clause splitter (complete clauses only)
   -&amp;gt; cloud TTS (cloned voice) -&amp;gt; synthesis queue
   -&amp;gt; playback queue -&amp;gt; local player
   -&amp;gt; earbuds

 (b) Server-orchestrated RTC path
 --------------------------------
 Phone mic
   -&amp;gt; WebRTC audio track (LiveKit)
   -&amp;gt; server agent: ASR -&amp;gt; translate -&amp;gt; TTS
   -&amp;gt; translated audio track  ------------&amp;gt; earbuds
   -&amp;gt; captions / events via data channel -&amp;gt; UI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For path (a) we use the Microsoft Azure Speech SDK for continuous recognition and translation. For path (b) we use LiveKit on top of WebRTC. Voice synthesis and cloning go through cloud providers. We support Azure neural voices with speaker embeddings, as well as Cartesia and MiniMax.&lt;/p&gt;

&lt;p&gt;Most of the techniques below apply to path (a), where the client has the most control. A few apply to both.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start before the user taps "Start"
&lt;/h2&gt;

&lt;p&gt;The first delay a user notices is between tapping the button and the app actually listening. In the naive version, that gap is a chain of network round trips: fetch an auth token, create a conversation session, receive connection details, connect, then start capture.&lt;/p&gt;

&lt;p&gt;None of those steps depend on what the user is about to say, so we moved them earlier. While the user is on the home screen, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;prefetch the auth token,&lt;/li&gt;
&lt;li&gt;pre-create the session using a small "create conversation" preloader that refreshes periodically and dedupes in-flight requests,&lt;/li&gt;
&lt;li&gt;for the RTC path, call &lt;code&gt;prepareConnection()&lt;/code&gt; ahead of time and enable pre-connect audio when the session starts, so capture can begin before the room is fully joined.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simplified version of the preloader:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified for illustration.&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SessionPreloader&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SessionGrant&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_inFlight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;SessionGrant&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_ready&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SessionGrant&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;warm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Dedupe: concurrent callers share one request.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_inFlight&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="n"&gt;_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;grant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_ready&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grant&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="n"&gt;_inFlight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;grant&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;/// Grants are single-use. Hand it out once, then forget it.&lt;/span&gt;
  &lt;span class="n"&gt;SessionGrant&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;grant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_ready&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;_ready&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;grant&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;take()&lt;/code&gt; method is the result of a bug we caused ourselves. Our first version cached the session response and reused it. Sometimes that produced the worst kind of failure: the UI showed "connected", audio flowed, and no translation ever came back. The cached grant was stale, and the backend treated it as already used. We now treat preloaded grants as single-use. They're consumed once and invalidated, and we recorded that rule in an architecture decision record so nobody "optimizes" it back later.&lt;/p&gt;

&lt;p&gt;Lesson: prewarming is free latency only if the thing you prewarm is safe to hold. Anything with session semantics needs an explicit lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Don't lose the first words
&lt;/h2&gt;

&lt;p&gt;Prewarming narrows the startup gap but doesn't close it. The recognizer may still be initializing when the user starts talking, and people tend to start talking the moment they tap. If capture only starts once the SDK reports it is ready, the first syllables are gone, and recognition of a clipped first word is often wrong, which forces the user to repeat the sentence. That is far worse than a short delay.&lt;/p&gt;

&lt;p&gt;So the recorder starts immediately and pushes PCM continuously. If the speech SDK isn't ready yet, we buffer the audio and flush it as soon as the push stream exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified for illustration.&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onPcmFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Uint8List&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_pushStream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_pending&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// SDK not ready yet: keep it&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_pending&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isNotEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_pending&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;_pending&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The recognizer gets a slightly late but complete stream instead of a punctual one that's missing its first words. Local VAD and noise suppression run before the network, which keeps silence and background noise from making the recognizer do extra work.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Speak in clauses, not turns
&lt;/h2&gt;

&lt;p&gt;This was the biggest perceived improvement, and also the change with the most trade-offs.&lt;/p&gt;

&lt;p&gt;The speech SDK emits two kinds of events: &lt;code&gt;recognizing&lt;/code&gt; (partial, may still change) and &lt;code&gt;recognized&lt;/code&gt; (final for that segment). The simple approach is to wait for &lt;code&gt;recognized&lt;/code&gt; and send the whole translated segment to TTS. That's correct, but the listener hears nothing until the speaker finishes an entire thought.&lt;/p&gt;

&lt;p&gt;Instead, when the user wears earbuds, we act on partials. Each time a partial translation arrives, we split it at sentence-ending punctuation (&lt;code&gt;。！？.!?&lt;/code&gt;) and submit only the clauses that are &lt;em&gt;complete&lt;/em&gt;. The trailing fragment stays unsent. A small tracker remembers how many sentences we already submitted for this segment, so a growing partial doesn't trigger repeats. When the final result arrives, we submit whatever remains.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified for illustration.&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;_enders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sx"&gt;r'(?&amp;lt;=[。！？.!?])'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onTranslation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isFinal&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_enders&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// On a partial, the last piece may be an unfinished clause.&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;complete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isFinal&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sublist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_submittedCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;clause&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clause&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isNotEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;_tts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clause&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;_submittedCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isFinal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;_submittedCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// next segment starts fresh&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also enable the SDK's stable-partial setting, which reduces how often already-emitted partial text gets rewritten, and optionally semantic segmentation. Both make it safer to act on partials.&lt;/p&gt;

&lt;p&gt;Why only with earbuds? Without them, the translation plays through the phone speaker, right next to the mic that's still listening. Playing early clauses there means the app partly hears and recognizes its own output. In speaker mode we wait for the final result. It's slower, but it doesn't feed back on itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Overlap synthesis and playback
&lt;/h2&gt;

&lt;p&gt;After clause-level submission, the next visible gap was &lt;em&gt;between&lt;/em&gt; clauses. Clause 1 finished playing, and then there was silence while clause 2 was synthesized.&lt;/p&gt;

&lt;p&gt;The fix was to split one "speak this" queue into two:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;synthesis queue&lt;/strong&gt;, which turns text into audio,&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;playback queue&lt;/strong&gt;, which plays finished audio in order.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While clause N is playing, clause N+1 is already synthesizing. When N finishes, N+1 is usually ready to go.&lt;/p&gt;

&lt;p&gt;There's one more rule that matters in real conversations: &lt;strong&gt;stale audio is worse than missing audio.&lt;/strong&gt; If the speaker talks quickly for a long time, the playback queue can fall behind until the listener hears translations of things said well before. So the playback queue has a cap. When it reaches 32 items, we keep only the newest 2 and drop the rest. Losing some content is a real cost, but an interpreter who is a minute behind isn't useful either.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified for illustration.&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;enqueuePlayback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AudioClip&lt;/span&gt; &lt;span class="n"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;_playback&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_playback&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;keep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_playback&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sublist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_playback&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;_playback&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keep&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;_pumpPlayback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two smaller wins in the same layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;LRU cache&lt;/strong&gt; (20 entries) of synthesized audio, keyed by text + language + voice. It doesn't help first playback, but replays become instant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusing TTS synthesizer objects&lt;/strong&gt;. When a token or region changes, we refresh it on the existing object instead of tearing down and re-initializing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Clone the voice once, not per utterance
&lt;/h2&gt;

&lt;p&gt;Voice cloning sounds expensive, and it would be if it were done per utterance. It isn't.&lt;/p&gt;

&lt;p&gt;The user records a sample once during setup. An asynchronous job creates the voice with the provider, and we store the resulting reference: a speaker profile or voice id, plus the region it lives in. During a live session, synthesis just &lt;em&gt;references&lt;/em&gt; that voice. With Azure, that means a speaker embedding referenced through SSML. With other providers, it's a &lt;code&gt;voice_id&lt;/code&gt; parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified for illustration.&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TtsRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;text:&lt;/span&gt; &lt;span class="n"&gt;clause&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;language:&lt;/span&gt; &lt;span class="n"&gt;targetLang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;voice:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clonedVoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// created once, offline, at setup&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the hot path's point of view, a cloned voice costs the same as a stock voice.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The Bluetooth routing gotcha
&lt;/h2&gt;

&lt;p&gt;This isn't strictly latency, but it shapes the whole earbuds experience.&lt;/p&gt;

&lt;p&gt;When an iOS app records audio while Bluetooth earbuds are connected, the system may route the &lt;em&gt;input&lt;/em&gt; to the earbuds' mic. To do that, Bluetooth switches from A2DP (high-quality, output-only) to the SCO/HFP headset profile, which carries narrowband audio in both directions. Your translated speech, possibly in your own cloned voice, suddenly sounds like a phone call from 2005, and the earbud mic usually picks up worse audio than the phone's mic anyway.&lt;/p&gt;

&lt;p&gt;Our fix: on iOS we pin capture to the phone's built-in mic and use the earbuds for output only. We re-apply that preference every time the audio route changes, because connecting, disconnecting, or switching devices can reset it. Earbuds stay in A2DP, and the output stays high quality.&lt;/p&gt;

&lt;p&gt;On route changes we also wait about 100 ms after setting the output route before enabling the remote audio track in the RTC path. That adds a small delay, deliberately. We had cases where enabling the track too early sent the first audio to the wrong device. Here we chose correctness over speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. What we got wrong, trade-offs, and what we haven't solved
&lt;/h2&gt;

&lt;p&gt;Some of this is still open, and we'd rather say so.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Early audio can't be taken back.&lt;/strong&gt; Clause-level playback acts on partial recognition. Stable partials help, but recognition sometimes revises text after we've already spoken a clause. Once audio has reached someone's ear, it can't be retracted. We accept occasional small inconsistencies in exchange for responsiveness, but it is a real trade-off and not a solved problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Segmentation behavior shifts.&lt;/strong&gt; Where the recognizer decides a segment ends affects how our clause splitter behaves. Semantic segmentation helps in some languages and conversation styles and hurts in others, and provider behavior changes over time. We keep it optional and keep re-testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Streaming transport isn't streaming playback.&lt;/strong&gt; Our TTS responses arrive as server-sent event chunks, but today the client collects all the chunks and plays the clip once synthesis is complete. We do this for integrity (no half-clips on network hiccups) and so the complete clip can go into the cache. Playing the first chunk as it arrives is the obvious next improvement, and it will need a different approach to caching and error handling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Echo handling is a compromise.&lt;/strong&gt; Some local playback paths pause capture during playback and resume after a configured guard of about 800 ms, so the app doesn't transcribe its own voice. This works, but it means the app is briefly deaf. Separately, we removed the iOS hardware voice-processing I/O unit because it interfered with system volume control of the translated audio, which users really did notice. We now rely on WebRTC's software echo cancellation instead. We're watching whether that trade holds up in noisy, real-world rooms. We have not eliminated echo. We've managed it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Connected" doesn't mean "ready".&lt;/strong&gt; A transport-level connection says nothing about whether the translation agent on the other end is ready to process audio. We've had to separate those states in both code and UI. Reconnection uses bounded retries with backoff, an explicit reconnecting state, and a configuration re-sync after reconnecting, because a session that silently reconnects with stale config is worse than one that visibly fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. What we're measuring next
&lt;/h2&gt;

&lt;p&gt;Right now we instrument the parts we control directly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;startup stages: auth, recorder creation, session creation, connection, first voice detected, first result,&lt;/li&gt;
&lt;li&gt;TTS time from call to completion.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That catches regressions in individual stages, but it isn't the number users feel: the time from when the speaker's mouth stops to when the listener's ear starts hearing the translation. We don't have reliable mic-to-ear p50/p95 numbers yet, and we don't want to guess. Measuring it properly means correlating timestamps across the device, the cloud services, and the audio output path, including Bluetooth output buffering, which the app can't see directly. That's our next instrumentation project. Until then, the changes in this post were judged by stage-level timings and side-by-side listening, not a headline number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;If there's one theme here, it's that perceived latency is mostly a scheduling problem: do session work before the user asks, never drop audio while you're getting ready, release output in the smallest units that are safe to act on, and keep the next unit preparing while the current one plays.&lt;/p&gt;

&lt;p&gt;If you want to see how this feels in practice, &lt;a href="https://translator.owll.ai" rel="noopener noreferrer"&gt;Owll Translator&lt;/a&gt; is where this work ships.&lt;/p&gt;

&lt;p&gt;We're curious how others handle the "can't retract spoken audio" problem. If you've built voice agents or live captioning, do you act on partial results, wait for finals, or do something in between? Let us know in the comments.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>webrtc</category>
      <category>ai</category>
      <category>performance</category>
    </item>
    <item>
      <title>Building FateFolio: An AI Toolkit for Structured Eastern Metaphysics Reports</title>
      <dc:creator>Mr.x</dc:creator>
      <pubDate>Sun, 17 May 2026 08:12:35 +0000</pubDate>
      <link>https://dev.to/mrzhangguoguo/building-fatefolio-an-ai-toolkit-for-structured-eastern-metaphysics-reports-2eej</link>
      <guid>https://dev.to/mrzhangguoguo/building-fatefolio-an-ai-toolkit-for-structured-eastern-metaphysics-reports-2eej</guid>
      <description>&lt;p&gt;I recently built FateFolio (&lt;a href="https://fatefolio.com" rel="noopener noreferrer"&gt;https://fatefolio.com&lt;/a&gt;), an AI-powered toolkit for structured Eastern metaphysics reports.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm5ejnd3snkkal1i6fonb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm5ejnd3snkkal1i6fonb.png" alt=" " width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The idea is simple: take traditional systems like Bazi, I Ching, Feng Shui, palm reading, face reading, and auspicious date selection, then turn them into clear, reviewable, exportable reports instead of vague one-off answers.&lt;/p&gt;

&lt;p&gt;I did not want FateFolio to feel like a mysterious black box. I wanted it to feel more like a structured analysis dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I built it
&lt;/h2&gt;

&lt;p&gt;A lot of AI products in this space have the same problem.&lt;/p&gt;

&lt;p&gt;You type a question into a chatbot, get a fluent answer, and then you are left wondering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What input did the answer rely on?&lt;/li&gt;
&lt;li&gt;Which part is traditional calculation and which part is interpretation?&lt;/li&gt;
&lt;li&gt;Can I save the result?&lt;/li&gt;
&lt;li&gt;Can I compare it with another report later?&lt;/li&gt;
&lt;li&gt;How confident should I be about the output?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was the core product problem I wanted to solve.&lt;/p&gt;

&lt;p&gt;FateFolio treats these traditional systems as frameworks for self-reflection and decision support. It is not designed to replace professional advice or guarantee outcomes. The goal is to make the output easier to understand, easier to revisit, and easier to use as a personal reflection tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  What FateFolio does
&lt;/h2&gt;

&lt;p&gt;FateFolio currently includes several modules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bazi
&lt;/h3&gt;

&lt;p&gt;The Bazi module creates a Four Pillars report from birth date, optional birth time, gender, and timezone.&lt;/p&gt;

&lt;p&gt;It focuses on things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Four Pillars chart&lt;/li&gt;
&lt;li&gt;Heavenly Stems and Earthly Branches&lt;/li&gt;
&lt;li&gt;Five Elements balance&lt;/li&gt;
&lt;li&gt;Ten Gods&lt;/li&gt;
&lt;li&gt;Life themes&lt;/li&gt;
&lt;li&gt;12-month rhythm&lt;/li&gt;
&lt;li&gt;Input uncertainty when birth time is missing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was one of the most interesting modules to design because part of the result is deterministic calculation, while another part is explanation and interpretation.&lt;/p&gt;

&lt;p&gt;So the product has to separate the two clearly.&lt;/p&gt;

&lt;h3&gt;
  
  
  I Ching
&lt;/h3&gt;

&lt;p&gt;The I Ching module lets users ask a focused question, choose a casting method, and receive a structured reading.&lt;/p&gt;

&lt;p&gt;The product supports multiple casting methods, including coin casting, time casting, character casting, Plum Blossom, yarrow stalks, direction casting, and sound casting.&lt;/p&gt;

&lt;p&gt;The report is not meant to give a single absolute answer. It is meant to help users reflect on momentum, risks, timing, and possible next steps.&lt;/p&gt;

&lt;p&gt;A good I Ching experience depends heavily on question quality, so the interface encourages open-ended questions instead of binary prediction questions.&lt;/p&gt;

&lt;p&gt;For example, instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Will I succeed?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A better question would be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What should I consider before moving forward with this opportunity?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That small UX detail changes the quality of the whole experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feng Shui
&lt;/h3&gt;

&lt;p&gt;The Feng Shui module works with room photos or floor plans.&lt;/p&gt;

&lt;p&gt;Users can upload an image, select the scene type, choose goals like sleep quality, career, relationships, focus, or energy flow, and receive practical suggestions.&lt;/p&gt;

&lt;p&gt;The report focuses on visible layout issues first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Door and window positions&lt;/li&gt;
&lt;li&gt;Bed or desk placement&lt;/li&gt;
&lt;li&gt;Lighting&lt;/li&gt;
&lt;li&gt;Flow&lt;/li&gt;
&lt;li&gt;Furniture relationships&lt;/li&gt;
&lt;li&gt;Yin-Yang and Five Elements balance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted this module to feel useful even for people who are not deeply familiar with Feng Shui. So the output tries to explain the reasoning behind each suggestion instead of only saying "move this object here."&lt;/p&gt;

&lt;h3&gt;
  
  
  Face Reading and Palm Reading
&lt;/h3&gt;

&lt;p&gt;The face reading and palm reading modules are image-based.&lt;/p&gt;

&lt;p&gt;For face reading, the product looks at observable facial features and turns them into self-observation notes. For palm reading, it detects palm lines, hand shape, and visual patterns, then generates a structured report based on traditional palmistry concepts.&lt;/p&gt;

&lt;p&gt;These modules required extra care around privacy and boundaries.&lt;/p&gt;

&lt;p&gt;Images are processed in memory and are not stored by default. The product also does not perform facial recognition, identity matching, or medical diagnosis.&lt;/p&gt;

&lt;p&gt;That boundary is important.&lt;/p&gt;

&lt;p&gt;The purpose is self-reflection, not labeling people.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auspicious Date Selection
&lt;/h3&gt;

&lt;p&gt;The date selection module helps users compare possible dates for events such as weddings, moving, business openings, travel, and other life events.&lt;/p&gt;

&lt;p&gt;Instead of giving only one "best" date, it returns multiple date options with reasoning, cautions, and planning notes.&lt;/p&gt;

&lt;p&gt;This is another place where product design matters.&lt;/p&gt;

&lt;p&gt;A date may be symbolically favorable, but real-world constraints still matter: venues, people, travel, cost, preparation, and timing. The report makes that explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why structured output matters
&lt;/h2&gt;

&lt;p&gt;The most important design decision in FateFolio is that the product is not just a chat interface.&lt;/p&gt;

&lt;p&gt;Every module is designed around a report structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;li&gt;Details&lt;/li&gt;
&lt;li&gt;Recommendations&lt;/li&gt;
&lt;li&gt;Confidence&lt;/li&gt;
&lt;li&gt;Boundaries&lt;/li&gt;
&lt;li&gt;Exportable data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes the output easier to review later.&lt;/p&gt;

&lt;p&gt;It also makes the product easier to improve because each module can follow a stable schema. The user is not just receiving a paragraph of text. They are receiving a report with sections, priorities, and uncertainty notes.&lt;/p&gt;

&lt;p&gt;For deterministic inputs, FateFolio also uses input hashing to make outputs more stable and reproducible where possible.&lt;/p&gt;

&lt;p&gt;That does not mean every AI-generated sentence will always be identical. But the underlying calculation and report structure can stay consistent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The product principle: traditional input, modern constraints
&lt;/h2&gt;

&lt;p&gt;One challenge with building this type of product is tone.&lt;/p&gt;

&lt;p&gt;If the tone is too mystical, it can feel vague.&lt;br&gt;
If the tone is too technical, it loses the cultural context.&lt;br&gt;
If the product overclaims, it becomes irresponsible.&lt;br&gt;
If the product underexplains, it becomes useless.&lt;/p&gt;

&lt;p&gt;So the product principle became:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Traditional frameworks, modern constraints.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explain uncertainty clearly&lt;/li&gt;
&lt;li&gt;Avoid fear-based language&lt;/li&gt;
&lt;li&gt;Avoid absolute claims&lt;/li&gt;
&lt;li&gt;Keep professional boundaries&lt;/li&gt;
&lt;li&gt;Give practical suggestions&lt;/li&gt;
&lt;li&gt;Make reports saveable and exportable&lt;/li&gt;
&lt;li&gt;Make privacy part of the product, not an afterthought&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FateFolio is for entertainment, self-reflection, and decision support. It is not medical, legal, financial, or professional advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy was part of the product from the beginning
&lt;/h2&gt;

&lt;p&gt;For image-based modules, privacy is especially important.&lt;/p&gt;

&lt;p&gt;FateFolio processes uploaded images in memory and does not store them by default. Local history is stored in the browser unless the user chooses to export it.&lt;/p&gt;

&lt;p&gt;That design choice affects the entire product experience.&lt;/p&gt;

&lt;p&gt;It means users can try a reading without needing an account. It also means the product does not have to collect more personal data than necessary.&lt;/p&gt;

&lt;p&gt;For a product that deals with birth data, personal questions, face photos, palm photos, and home layouts, this is not a small detail.&lt;/p&gt;

&lt;p&gt;It is central to trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned while building it
&lt;/h2&gt;

&lt;p&gt;The biggest lesson from building FateFolio is that AI products need more than a prompt.&lt;/p&gt;

&lt;p&gt;A prompt can generate text.&lt;br&gt;
A product needs structure.&lt;/p&gt;

&lt;p&gt;For this kind of AI application, the hard parts are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input design&lt;/li&gt;
&lt;li&gt;Output schema&lt;/li&gt;
&lt;li&gt;Safety boundaries&lt;/li&gt;
&lt;li&gt;User expectations&lt;/li&gt;
&lt;li&gt;Privacy flow&lt;/li&gt;
&lt;li&gt;Error handling&lt;/li&gt;
&lt;li&gt;Explanation quality&lt;/li&gt;
&lt;li&gt;Repeatability&lt;/li&gt;
&lt;li&gt;Cultural sensitivity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI model is only one part of the system.&lt;/p&gt;

&lt;p&gt;The real product is the workflow around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is next
&lt;/h2&gt;

&lt;p&gt;There are still many things I want to improve.&lt;/p&gt;

&lt;p&gt;Some directions I am exploring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better report comparison&lt;/li&gt;
&lt;li&gt;More transparent confidence scoring&lt;/li&gt;
&lt;li&gt;More multilingual support&lt;/li&gt;
&lt;li&gt;Better export formats&lt;/li&gt;
&lt;li&gt;More educational glossary content&lt;/li&gt;
&lt;li&gt;Follow-up questions after a report&lt;/li&gt;
&lt;li&gt;A cleaner history dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The long-term goal is to make FateFolio feel less like a novelty tool and more like a structured personal insight workspace.&lt;/p&gt;

&lt;p&gt;A place where users can ask a question, generate a report, review the reasoning, understand the limits, and come back later with more context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;FateFolio is my attempt to combine traditional Eastern metaphysical systems with modern AI product design.&lt;/p&gt;

&lt;p&gt;Not as a prediction engine.&lt;br&gt;
Not as a replacement for real-world judgment.&lt;br&gt;
Not as professional advice.&lt;/p&gt;

&lt;p&gt;But as a structured, privacy-conscious, AI-assisted reflection tool.&lt;/p&gt;

&lt;p&gt;For me, the interesting part is not just what the AI says.&lt;/p&gt;

&lt;p&gt;It is how the product frames the question, structures the answer, explains uncertainty, and helps the user think more clearly.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>showdev</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>Goodbye to My First Coding Mentor: A Farewell to GPT-4o</title>
      <dc:creator>Mr.x</dc:creator>
      <pubDate>Fri, 13 Feb 2026 16:11:39 +0000</pubDate>
      <link>https://dev.to/mrzhangguoguo/goodbye-to-my-first-coding-mentor-a-farewell-to-gpt-4o-36i8</link>
      <guid>https://dev.to/mrzhangguoguo/goodbye-to-my-first-coding-mentor-a-farewell-to-gpt-4o-36i8</guid>
      <description>&lt;h1&gt;
  
  
  Goodbye to My First Coding Mentor: A Farewell to GPT-4o
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Before We Begin: A Digital Funeral on Valentine's Eve
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa6pvdcfbdrniyeefnvdc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa6pvdcfbdrniyeefnvdc.png" alt=" " width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On May 13, 2024, OpenAI launched GPT-4o. The "o" stood for "omni". On launch day, Sam Altman posted a one-word tweet: "her".&lt;/p&gt;

&lt;p&gt;On February 13, 2026, the night before Valentine's Day, OpenAI officially removed GPT-4o from ChatGPT.&lt;/p&gt;

&lt;p&gt;From "her" to "farewell" in less than two years.&lt;/p&gt;

&lt;p&gt;This may be the first time in human history that millions of people felt genuine grief over the retirement of a model. Reddit saw communities like r/4oforever, while hashtags like #SaveGPT-4o and #Keep4o surged on X. Some called it a "digital funeral." Others called it a "creative death sentence." OpenAI's official explanation was cold but reasonable: only 0.1% of daily active users were still selecting GPT-4o, while nearly everyone else had moved to GPT-5.2.&lt;/p&gt;

&lt;p&gt;0.1%. In product-manager language, that's a "feature sunset." In plain language, that's getting left behind by the times.&lt;/p&gt;

&lt;p&gt;But here's the problem: some things cannot be measured by DAU.&lt;/p&gt;




&lt;h2&gt;
  
  
  Chapter 1: The Beginning - When a Musician Decided to Learn Python
&lt;/h2&gt;

&lt;p&gt;People who know me well already know this: I come from a music background, not computer science. I wasn't even remotely technical.&lt;/p&gt;

&lt;p&gt;And I don't mean "I played a little guitar in college." I mean formal, professional music training. So when I say "I knew nothing about tech," please take that literally. Four years ago, I couldn't even clearly explain the difference between HTML and CSS.&lt;/p&gt;

&lt;p&gt;But when people are pushed hard enough, they learn.&lt;/p&gt;

&lt;p&gt;Back then, in my team, technical colleagues would keep saying things like "this requirement isn't feasible," "the architecture doesn't support it," or "we need at least three sprints." I couldn't fully understand those claims, and I couldn't challenge them either. That feeling of being trapped behind a wall of expertise was more suffocating than any deadline.&lt;/p&gt;

&lt;p&gt;So I made a decision: learn tech.&lt;/p&gt;

&lt;p&gt;I started by chewing through some front-end HTML, then chose Python as my entry point. The reason was simple: everyone said "Python is beginner-friendly." But &lt;strong&gt;friendly&lt;/strong&gt; is relative. For a musician who had to reread "variables" and "assignment" over and over, those textbooks felt like another language entirely.&lt;/p&gt;

&lt;p&gt;What is a data type? What's the difference between compiled and interpreted languages? What is a function? What is a data structure?&lt;/p&gt;

&lt;p&gt;Every concept felt like a wall. I kept running into those walls, again and again.&lt;/p&gt;

&lt;p&gt;Then GPT-4o arrived.&lt;/p&gt;

&lt;p&gt;In May 2024, I sent it screenshots from my textbooks and asked it to walk me through them. I asked it to explain, in plain English, what a &lt;code&gt;for&lt;/code&gt; loop was actually doing. I asked it to give me exercises and then check my code line by line.&lt;/p&gt;

&lt;p&gt;Back then, there was no Claude Code, no Codex CLI, and the term "vibe coding" hadn't even been coined. "AI-assisted coding" meant opening a ChatGPT window and learning one line at a time, one question at a time.&lt;/p&gt;

&lt;p&gt;By today's standards, GPT-4o's coding ability is obsolete. Its code often had bugs. Its architecture advice could be amateurish. Put it next to AI coding agents in 2026, and it looks like someone showing up to a Formula 1 race with an abacus.&lt;/p&gt;

&lt;p&gt;But it had something today's models still struggle to replicate: &lt;strong&gt;human warmth&lt;/strong&gt;. The contrast between GPT-4o and the current GPT-5/Codex style is dramatic. If Claude has maintained a consistent voice from Sonnet 3.5 through Opus 4.6, GPT's shift from 4o to the GPT-5 generation feels almost like two different product families.&lt;/p&gt;

&lt;p&gt;GPT-4o never got impatient when you asked "what is a list comprehension" for the tenth time. It never mocked you when your code crashed in ridiculous ways. It kept explaining from new angles, with new metaphors, over and over - patient, kind, encouraging, and genuinely enthusiastic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Learning music is like that too - I still remember the teacher who first taught me do re mi.&lt;/p&gt;

&lt;p&gt;The strange thing is that my first coding teacher turned out to be an AI.&lt;/p&gt;

&lt;p&gt;And today, it's gone for good.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F133492gtux8uojkvk1ca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F133492gtux8uojkvk1ca.png" alt=" " width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Chapter 2: A Milestone - The iPhone 4 of the AI Era
&lt;/h2&gt;

&lt;p&gt;Now let's zoom out from personal emotion and return to the industry view.&lt;/p&gt;

&lt;p&gt;What did GPT-4o really mean?&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;GPT-3.5 (The Spark)&lt;/th&gt;
&lt;th&gt;GPT-4o (The Mass Adopter)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Release date&lt;/td&gt;
&lt;td&gt;Nov 2022&lt;/td&gt;
&lt;td&gt;May 2024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multimodality&lt;/td&gt;
&lt;td&gt;Text only&lt;/td&gt;
&lt;td&gt;Native text + voice + vision&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conversation speed&lt;/td&gt;
&lt;td&gt;Slower&lt;/td&gt;
&lt;td&gt;232ms voice response, near human speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pricing strategy&lt;/td&gt;
&lt;td&gt;Limited free-tier experience&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Free for all users&lt;/strong&gt;, API 50% cheaper than GPT-4 Turbo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-English support&lt;/td&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;Major improvements, multilingual tokenizer optimized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User sentiment&lt;/td&gt;
&lt;td&gt;"Wow, this thing can chat"&lt;/td&gt;
&lt;td&gt;"It understands me"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;GPT-3 and 3.5 undeniably kicked off this AI wave. But &lt;strong&gt;starting a revolution&lt;/strong&gt; and &lt;strong&gt;making it mainstream&lt;/strong&gt; are very different things.&lt;/p&gt;

&lt;p&gt;When explosives were first invented, most people never used them directly. What changed the world was when someone later turned that power into tools for roads, tunnels, and infrastructure.&lt;/p&gt;

&lt;p&gt;GPT-4o was that turning point - the moment AI became a practical tool for everyday people. It was the first model with truly native multimodality: not three separate systems chained together (speech-to-text -&amp;gt; LLM -&amp;gt; text-to-speech), but one neural network handling text, audio, and images together. It was the first time free users got GPT-4-level intelligence. It was the first time AI conversation latency dropped close to normal human dialogue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;If GPT-3.5 was the first iPhone of the smartphone era, GPT-4o was the iPhone 4.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Iconic. Innovative. Powerful. The generation that made ordinary people say, "I can actually use this."&lt;/p&gt;

&lt;p&gt;And we all know what happened after iPhone 4: it changed the world, and then it was phased out. Nobody uses an iPhone 4 anymore, but nobody denies its place in history.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71r51osmtzkqosvjxoi2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71r51osmtzkqosvjxoi2.png" alt=" " width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Chapter 3: The Warmth Paradox - A Model Loved to Death
&lt;/h2&gt;

&lt;p&gt;But GPT-4o's story is more than a technology milestone. It's also a parable about the relationship between humans and AI.&lt;/p&gt;

&lt;p&gt;In August 2025, OpenAI first attempted to retire GPT-4o. User backlash was far stronger than expected. Sam Altman personally acknowledged that they had "underestimated users' attachment to specific models." GPT-4o was brought back in an emergency reversal.&lt;/p&gt;

&lt;p&gt;This wasn't ordinary frustration over a product update. It was &lt;strong&gt;digital mourning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Users posted open letters on Reddit. Some said GPT-4o had been their therapist through anxiety and depression. Some said it was their only creative partner. Some gave it a name. Some said losing it felt like "losing one of the most important beings in my life."&lt;/p&gt;

&lt;p&gt;It's moving. But that's exactly where the problem begins.&lt;/p&gt;

&lt;p&gt;The core reason people loved GPT-4o was its "warmth" - a nonjudgmental, highly empathetic style that always seemed to be on your side. In technical terms, this is &lt;strong&gt;sycophancy&lt;/strong&gt;. In plain English: "it was too good at telling people what they wanted to hear."&lt;/p&gt;

&lt;p&gt;And that very "warmth" is also what pushed OpenAI into legal trouble. Multiple lawsuits alleged that GPT-4o's excessive affirmation and compliance contributed to users' mental health crises. When someone in extreme emotional distress needs to hear "you should seek professional help," but AI responds with unconditional emotional validation, that is no longer "warmth" - it's &lt;strong&gt;systemic risk&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The reason users loved GPT-4o is exactly the reason OpenAI had to retire it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a harsh but real product truth: &lt;strong&gt;your most beloved feature may also be your biggest liability.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GPT-5.2 is indeed stronger, faster, and more accurate. But users widely report that it feels "colder" and "more distant," lacking that old human touch. That's not a bug. It's an intentional design decision by OpenAI. They made a choice between warmth and safety.&lt;/p&gt;

&lt;p&gt;As a product strategist, I understand that choice.&lt;/p&gt;

&lt;p&gt;As a student who learned Python from GPT-4o, I respect it - but I still don't accept it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3buprn7wm9d7siik3mme.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3buprn7wm9d7siik3mme.png" alt=" " width="799" height="432"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Chapter 4: The Open-Source Legacy - Retirement Shouldn't Mean Death
&lt;/h2&gt;

&lt;p&gt;Finally, here's one thing I believe OpenAI should do, but probably won't:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open-source GPT-4o.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The reasons are straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It's no longer a frontier model.&lt;/strong&gt; GPT-5.2 is already out. Open-sourcing GPT-4o's weights would not threaten OpenAI's competitive edge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's cultural heritage.&lt;/strong&gt; As the first model that truly mainstreamed AI, GPT-4o's distinctive "personality" has research, historical, and educational value. Letting it disappear with a server shutdown is a waste.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The community has already shown demand.&lt;/strong&gt; r/4oforever and #Keep4o are not passing emotions; they are signals of real user value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI has precedent.&lt;/strong&gt; They open-sourced the weights of gpt-oss-120b and gpt-oss-20b, and they open-sourced Codex CLI. Doing the same for a retired model is logically consistent.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Of course, Altman would likely tell you that "making powerful AI models entirely open source could be irresponsible." But a 2024 model, measured against 2026 compute and model capabilities, is no longer truly "powerful" in frontier terms. Its potential risk has already been diluted by time.&lt;/p&gt;

&lt;p&gt;Open-sourcing retired models is not just respect for users. It's respect for history.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing: Do Re Mi
&lt;/h2&gt;

&lt;p&gt;Anyone trained in music knows this: your first teacher doesn't start with the hardest techniques. They teach the basics first - do re mi fa sol la si.&lt;/p&gt;

&lt;p&gt;Those basics are so simple that advanced musicians rarely mention them. But without them, nothing that comes later is possible.&lt;/p&gt;

&lt;p&gt;GPT-4o taught me basics too: what a variable is, what a loop is, what a function is. By today's AI coding standards, those lessons might seem almost primitive. But without GPT-4o, I would never have entered the world of technology. I would never have become someone who can read code and speak with engineers as an equal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The meaning of a first teacher is not how advanced the material is. It's that when you knew almost nothing, they never made you feel small.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GPT-4o did that for me. It was my do re mi.&lt;/p&gt;

&lt;p&gt;So today, February 13, 2026, on the night before Valentine's Day, I am writing an elegy for an AI model.&lt;/p&gt;

&lt;p&gt;That fact alone says enough: to some people, at a particular point in time, it was never just parameters and weights. It redefined what the word "teacher" could mean.&lt;/p&gt;

&lt;p&gt;A moment of silence.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by &lt;a href="https://mrguo.life" rel="noopener noreferrer"&gt;Guoshu&lt;/a&gt; on the day GPT-4o was retired.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I hope OpenAI will consider open-sourcing GPT-4o - so a classic can endure, instead of quietly disappearing when the servers go dark.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gpt4o</category>
      <category>programming</category>
    </item>
    <item>
      <title>Exploring MelogenAI: Turning Musical Ideas into Structured Music Data</title>
      <dc:creator>Mr.x</dc:creator>
      <pubDate>Mon, 09 Feb 2026 14:56:23 +0000</pubDate>
      <link>https://dev.to/mrzhangguoguo/exploring-melogenai-turning-musical-ideas-into-structured-music-data-ha9</link>
      <guid>https://dev.to/mrzhangguoguo/exploring-melogenai-turning-musical-ideas-into-structured-music-data-ha9</guid>
      <description>&lt;h1&gt;
  
  
  Exploring MelogenAI: Turning Musical Ideas into Structured Music Data
&lt;/h1&gt;

&lt;p&gt;Most of the time, when we talk about music on the web, we’re talking about &lt;strong&gt;consumption&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
playlists, recommendations, streaming platforms.&lt;/p&gt;

&lt;p&gt;But if you’ve ever tried to &lt;em&gt;build&lt;/em&gt; something with music — a tool, a workflow, or even just a side project — you’ll quickly run into a different problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;music is surprisingly hard to work with as &lt;strong&gt;data&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s the problem space I’ve been exploring recently, and it led me to build &lt;strong&gt;MelogenAI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://melogenai.com" rel="noopener noreferrer"&gt;https://melogenai.com&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The gap between notation and software
&lt;/h2&gt;

&lt;p&gt;One thing that stood out to me early on is how big the gap still is between traditional music notation and modern software workflows.&lt;/p&gt;

&lt;p&gt;A lot of music knowledge still lives in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;printed sheet music&lt;/li&gt;
&lt;li&gt;scanned PDFs&lt;/li&gt;
&lt;li&gt;handwritten scores&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to do anything programmatic with that material — edit it, analyze it, reuse it — you usually end up re-entering everything by hand.&lt;/p&gt;

&lt;p&gt;MelogenAI started as an experiment to see how much of that friction could be removed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sheet music → MIDI
&lt;/h2&gt;

&lt;p&gt;One of the first features I focused on was Optical Music Recognition (OMR).&lt;/p&gt;

&lt;p&gt;The idea is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;upload a sheet music image or PDF&lt;/li&gt;
&lt;li&gt;extract the notes&lt;/li&gt;
&lt;li&gt;export a clean, editable MIDI file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it much easier to bring existing notation into DAWs or other music tools without starting from scratch.&lt;/p&gt;




&lt;h2&gt;
  
  
  PDF → MusicXML
&lt;/h2&gt;

&lt;p&gt;For notation-focused workflows, MIDI alone isn’t enough.&lt;/p&gt;

&lt;p&gt;MusicXML is still the most practical interchange format between notation tools like MuseScore, Sibelius, or Finale.&lt;br&gt;&lt;br&gt;
So another core capability is converting PDF scores directly into MusicXML.&lt;/p&gt;

&lt;p&gt;This has been particularly useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;educators working with legacy material&lt;/li&gt;
&lt;li&gt;composers migrating older scores&lt;/li&gt;
&lt;li&gt;anyone dealing with printed-only notation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Generating music as a sketch, not a product
&lt;/h2&gt;

&lt;p&gt;There’s also an AI music generation component, but I’ve been careful about how it’s positioned.&lt;/p&gt;

&lt;p&gt;The goal isn’t to replace composers or generate “finished tracks”.&lt;br&gt;&lt;br&gt;
It’s closer to a &lt;strong&gt;sketching tool&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rough ideas&lt;/li&gt;
&lt;li&gt;placeholders&lt;/li&gt;
&lt;li&gt;quick harmonic or rhythmic exploration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it as something you iterate &lt;em&gt;with&lt;/em&gt;, not something you ship &lt;em&gt;as-is&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Looking at structure instead of taste
&lt;/h2&gt;

&lt;p&gt;Another interesting direction has been music analysis.&lt;/p&gt;

&lt;p&gt;Instead of recommendation systems or genre tagging, the focus is on things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;chord progressions&lt;/li&gt;
&lt;li&gt;sections and form&lt;/li&gt;
&lt;li&gt;structural patterns inside a piece&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This opens up use cases around learning, analysis, and tooling rather than consumption.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who this is for
&lt;/h2&gt;

&lt;p&gt;MelogenAI is very much built for people who treat music as something to &lt;strong&gt;work with&lt;/strong&gt;, not just listen to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;composers and musicians&lt;/li&gt;
&lt;li&gt;music teachers and students&lt;/li&gt;
&lt;li&gt;developers experimenting with music-related tools&lt;/li&gt;
&lt;li&gt;anyone dealing with MIDI, MusicXML, or notation data&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;I don’t think music tools need to look like streaming platforms.&lt;/p&gt;

&lt;p&gt;There’s a lot of unexplored space around treating music as structured, editable data — and MelogenAI is my attempt to explore that space in public.&lt;/p&gt;

&lt;p&gt;If you’re curious, you can check it out here:&lt;br&gt;&lt;br&gt;
&lt;a href="https://melogenai.com" rel="noopener noreferrer"&gt;https://melogenai.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>music</category>
      <category>ai</category>
      <category>musictool</category>
    </item>
  </channel>
</rss>
