<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[AISongsMaker Engineering]]></title><description><![CDATA[Product and engineering notes from building AISongsMaker, a music workspace for creators. Practical lessons on frontend state, clear interactions, and creative ]]></description><link>https://aisongsmaker.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6acf9e250038631962ab38de/615437ff-f3fb-44b8-bf8d-00100899617c.png</url><title>AISongsMaker Engineering</title><link>https://aisongsmaker.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 24 Oct 2026 16:30:39 GMT</lastBuildDate><atom:link href="https://aisongsmaker.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Browsing Another Song Shouldn't Interrupt the One You're Playing]]></title><description><![CDATA[Separate playback, inspected details, and creation-source state so browsing another song does not interrupt the one a creator is hearing.]]></description><link>https://aisongsmaker.hashnode.dev/separate-browsing-playback-and-source-state</link><guid isPermaLink="true">https://aisongsmaker.hashnode.dev/separate-browsing-playback-and-source-state</guid><category><![CDATA[Frontend Development]]></category><category><![CDATA[UX]]></category><dc:creator><![CDATA[Sophia Chen]]></dc:creator><pubDate>Thu, 15 Oct 2026 13:46:17 GMT</pubDate><content:encoded><![CDATA[<p>A creator is listening to one version of a song and opens another version's lyrics. Should the music change?</p>
<p>In a music workspace, that interaction exposes a state-design problem. Reading, listening, and choosing a source for the next creative action are related activities, but they are different decisions.</p>
<p>At AISongsMaker, we treat those decisions separately for completed songs. This article explains the state boundary behind that behavior. The code below is a simplified example, not a copy of our production implementation.</p>
<h2>One selected ID can carry too much meaning</h2>
<p>A single <code>selectedTrackId</code> looks convenient. The highlighted row, details panel, audio element, and continuation form can all read from it.</p>
<p>The trouble starts when clicking a row means “show me the lyrics,” while the audio element interprets the same change as “play this song.” A navigation action becomes a playback action. If a continuation form also reads that ID, inspecting a different result can quietly change the source of the next operation.</p>
<p>Consider three songs:</p>
<table>
<thead>
<tr>
<th>Responsibility</th>
<th>Song</th>
<th>What the creator is doing</th>
</tr>
</thead>
<tbody><tr>
<td>Player</td>
<td>A</td>
<td>Listening to an arrangement</td>
</tr>
<tr>
<td>Details</td>
<td>B</td>
<td>Reading another version's lyrics</td>
</tr>
<tr>
<td>Creation source</td>
<td>C</td>
<td>Preparing a continuation from an earlier result</td>
</tr>
</tbody></table>
<p>This is a valid workspace state. It should not need three browser tabs.</p>
<h2>Give each action a smaller contract</h2>
<p>Use state names that describe their responsibility:</p>
<pre><code class="language-ts">type TrackId = string;

type WorkspaceState = {
  playingTrackId: TrackId | null;
  detailTrackId: TrackId | null;
  sourceTrackId: TrackId | null;
};

// Illustrative handlers; not a full player implementation.
function inspectTrack(id: TrackId) {
  setDetailTrackId(id);
  setDetailsOpen(true);
}

function requestPlayback(id: TrackId) {
  setPlayingTrackId(id);
}

function prepareContinuation(id: TrackId) {
  setSourceTrackId(id);
}
</code></pre>
<p>The important part is what these handlers are allowed to change. Inspecting lyrics does not assign an audio source. Preparing a continuation does not require starting playback.</p>
<p>Explicit preview controls can have a different contract. A public example's preview button is an invitation to listen, so changing the player there is reasonable. The boundary applies to inspection actions; it is not a rule that every click must preserve playback.</p>
<h2>Make the separation visible</h2>
<p>Separate state is useful only if the interface tells the same story.</p>
<p>The player should identify the song it is actually playing. A details panel should identify the song being inspected. A continuation form should show its source rather than making the creator infer it from whichever row is highlighted.</p>
<p>On mobile, opening details can change the visible screen without changing the audio. Returning to the list should restore navigation context. If opening and closing panels moves focus, preserve a sensible return target for keyboard users too.</p>
<p>Per-track playback position is another independent concern. When a creator explicitly switches back to a song, a saved position can help them resume a comparison. That position should belong to the song being played, not the most recently inspected song.</p>
<h2>Check the boundaries, not just the happy path</h2>
<p>A useful regression sequence is to play A, open B's details, and verify that A remains the player source and its playback continues. Then prepare an action from C and check that the form identifies C without silently replacing either of the other contexts.</p>
<p>Test explicit playback separately: starting B should update the player. Also cover a missing or deleted source, closing details with the keyboard, and returning from details on a narrow screen. Clear unavailable state honestly rather than falling back to an unrelated song.</p>
<p>This design does not establish a retention improvement or prove that every music product needs the same layout. It gives each action a clear responsibility. Before using a single “selected item” in a creative application, ask which decision that selection actually represents.</p>
]]></content:encoded></item></channel></rss>