<?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: Paul Gendek</title>
    <description>The latest articles on DEV Community by Paul Gendek (@paulgendek).</description>
    <link>https://dev.to/paulgendek</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%2F4155797%2F99af62cc-60d6-4cbe-a37c-9912af293ddf.jpg</url>
      <title>DEV Community: Paul Gendek</title>
      <link>https://dev.to/paulgendek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paulgendek"/>
    <language>en</language>
    <item>
      <title>Confirming Barcodes Across Missed Camera Frames</title>
      <dc:creator>Paul Gendek</dc:creator>
      <pubDate>Tue, 13 Oct 2026 16:10:42 +0000</pubDate>
      <link>https://dev.to/paulgendek/confirming-barcodes-across-missed-camera-frames-17jl</link>
      <guid>https://dev.to/paulgendek/confirming-barcodes-across-missed-camera-frames-17jl</guid>
      <description>&lt;p&gt;A camera can show a live image while a barcode scanner never completes a capture. I encountered that failure in &lt;a href="https://scantrastapp.com/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=context_matters_2026_10" rel="noopener noreferrer"&gt;Scantrast&lt;/a&gt;, an app that reads food-product barcodes and interprets nutrition against a person's targets. The problem was in the rule used to confirm a decoded result.&lt;/p&gt;

&lt;p&gt;The original confirmation rule required consecutive matching reads. An unreadable frame cleared the candidate. That seemed reasonable as a way to reject an isolated incorrect decode, and automated tests could establish that the rule worked as specified.&lt;/p&gt;

&lt;p&gt;The sequence that mattered on the phone was different:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;frame 1: readable code A
frame 2: no readable code
frame 3: readable code A
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A and B in this article are symbolic labels for valid, normalized barcode identities, not real product data. Under the consecutive-read rule, frame 2 erased the progress made on frame 1. Frame 3 started over. Repeated interruptions could leave the camera looking functional while confirmation never finished.&lt;/p&gt;

&lt;p&gt;The acceptance requirement needed to tolerate an intermittent observation. We changed it to two matching sightings within a two-second window. An unreadable frame could occur between them. A different valid code started a new candidate, and an expired candidate could not contribute an old sighting to a later confirmation.&lt;/p&gt;

&lt;p&gt;Those boundaries matter. Keeping a candidate forever could combine observations made too far apart. Combining different codes would weaken identity. Counting the same image twice would provide less evidence than two sightings from distinct frames. The corrected requirement addressed all three concerns while allowing a missed read.&lt;/p&gt;

&lt;p&gt;The two-second value is the choice made for this scanner, not a universal recommendation for cameras. A different capture rate, decoder or interaction could justify a different threshold. Two matching sightings are also a confirmation heuristic, not proof that a decoder can never produce a repeated wrong result.&lt;/p&gt;

&lt;p&gt;Here is one way to explain the state machine. Assume an upstream frame gate calls this only for fresh frames, supplies a monotonic timestamp, and passes either a valid normalized code or no code. That gate is a separate responsibility. The real implementation separates frame tracking from barcode confirmation; this pseudocode focuses on the confirmation decision.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;candidate = none
window = 2000 milliseconds

observe(code, now):
    if candidate exists and now - candidate.startedAt &amp;gt; window:
        candidate = none

    if code is none:
        return pending

    if candidate is none or code != candidate.code:
        candidate = { code, startedAt: now }
        return pending

    result = candidate.code
    candidate = none
    return confirmed(result)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An unreadable fresh frame leaves an unexpired candidate intact. A different code replaces it. A second matching code confirms only if the candidate is still within its window. On confirmation, the surrounding scan operation should handle delivery once and end or transition deliberately; repeatedly accepting results from a still-running loop is a separate concern.&lt;/p&gt;

&lt;p&gt;Frame identity deserves its own tests. The camera's media time alone was an unsuitable way to assume a new image had arrived. The corrected Scantrast frame tracker uses composited-frame callbacks where available, with presented-frame counts and media time as fallbacks. These routes have different strengths; a fallback does not certify fresh physical images on every browser. The guard's job is to keep a frozen image from supplying both confirmation sightings.&lt;/p&gt;

&lt;p&gt;Useful regression checks exercise sequences rather than only the confirmation count:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A fresh A, a miss, then another fresh A within the window should confirm. This is the observation that invalidated the original rule.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A single A followed by misses should remain pending, then expire. A later A should seed a new candidate rather than inherit the old sighting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A, then B, then A should not confirm A from its first appearance. Each different valid identity starts a new candidate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A repeated decode of a frozen frame should not reach confirmation. Fresh frames should remain usable even when the media timeline does not advance as expected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;At the window boundary, test the chosen comparison explicitly. The implementation expires a candidate after the window; avoid leaving exact-boundary behavior accidental.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run the interrupted sequence through decoding and the scan loop as well as the small confirmation helper. A helper can behave correctly while a frame gate or asynchronous loop prevents it from receiving the expected observations.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The engineering record includes an unreadable frame between successful reads, confirmation expiry, duplicate-frame rejection and fresh-frame behavior with unchanged media time. The original regression checks were observed failing before the correction. That failing-before-fix observation connected the checks to the reported behavior, rather than merely showing that the revised code passed a newly written test.&lt;/p&gt;

&lt;p&gt;Camera lifecycle introduces another class of acceptance conditions. Pausing, switching cameras, closing the dialog or backgrounding the app can interrupt acquisition, decoding or a product lookup. A result arriving after cancellation must not silently complete the previous interaction. A stream arriving after its request has been abandoned needs cleanup rather than ownership by a closed view.&lt;/p&gt;

&lt;p&gt;Automated frame and stream doubles can exercise cancellation, timeouts and late results. Physical-device review is still needed for optics, focus, camera choice and actual image delivery. During this correction I confirmed capture worked in the preview on my phone. That is a bounded observation, not a claim that every camera, low-light condition or recovery sequence was tested successfully.&lt;/p&gt;

&lt;p&gt;The regression checks preserve the failed sequence alongside identity changes, expiry and cancellation. Two uninterrupted reads would have passed the original rule while missing the failure I saw on my phone.&lt;/p&gt;

&lt;p&gt;This expands the scanner example in &lt;a href="https://paulgendek.com/writing/context-matters?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=context_matters_2026_10" rel="noopener noreferrer"&gt;Context Matters&lt;/a&gt;. The &lt;a href="https://paulgendek.com/projects/scantrast/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=context_matters_2026_10" rel="noopener noreferrer"&gt;Scantrast overview&lt;/a&gt; describes the product context; this article focuses on the confirmation and acceptance lesson.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;I used AI assistance to draft and edit this article. I supplied the requirements, device observations and acceptance judgment; coding agents implemented changes and ran automated checks. The pseudocode above is an illustrative explanation, not a copy of production code.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Designing browser music controls around the next beat</title>
      <dc:creator>Paul Gendek</dc:creator>
      <pubDate>Thu, 01 Oct 2026 21:22:57 +0000</pubDate>
      <link>https://dev.to/paulgendek/designing-browser-music-controls-around-the-next-beat-29f6</link>
      <guid>https://dev.to/paulgendek/designing-browser-music-controls-around-the-next-beat-29f6</guid>
      <description>&lt;p&gt;When I am playing guitar alone, changing a backing groove usually means moving a hand from the instrument to the laptop. A button can be easy to click and still interrupt the activity it is supposed to support. That problem led me to Mock Band, a browser rhythm section, and to a useful distinction: accepting a command, applying a musical change and making that change understandable to the player are separate jobs.&lt;/p&gt;

&lt;p&gt;I supplied the product direction and requirements, tested physical hardware and retained merge approval. AI tools implemented changes and ran automated checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with what the player is doing
&lt;/h2&gt;

&lt;p&gt;The initial idea, recorded in a note in September 2023, was to choose drums, bass and guitar accompaniment, repeat a groove and then reshape it through instructions. A player could change key or move to half-time while keeping both hands on the guitar. A footswitch would activate voice listening.&lt;/p&gt;

&lt;p&gt;The player remained responsible for the riff and musical direction. The product did not need to listen to the guitar and infer its performance to serve that goal. It needed to accept an intentional change with less interruption.&lt;/p&gt;

&lt;p&gt;That distinction reduced the interaction problem to something concrete. How does a person indicate a change? Which parts of that change can happen now? Which should wait for a musical boundary? What should remain usable when voice or a controller is unavailable?&lt;/p&gt;

&lt;p&gt;I chose the browser after researching WebMIDI and browser music APIs. Voice recognition, MIDI support and permissions still vary across browser and device combinations. Visible controls and text therefore remain useful ways to operate the band.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect input methods to shared musical actions
&lt;/h2&gt;

&lt;p&gt;The interface and application state use React and TypeScript. Text and recognized speech share a command vocabulary. A typed tempo instruction and its recognized spoken equivalent can reach the same musical action without maintaining two separate definitions of what the instruction means.&lt;/p&gt;

&lt;p&gt;MIDI controller input has a different shape. A mapped controller action can invoke a scene or transport handler directly. It does not need to become a sentence and pass through a text parser first.&lt;/p&gt;

&lt;p&gt;The useful point of convergence is shared application state and provider handlers. Those handlers connect transport, musical changes and scene events to the rest of the system. Sharing that destination gives the input methods a common effect while preserving the differences in how the request arrived.&lt;/p&gt;

&lt;p&gt;This matters when extending a product. Adding an input method should not require inventing another version of scene recall or tempo changes. It also should not require squeezing every physical gesture into a text-command metaphor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give audio timing its own responsibility
&lt;/h2&gt;

&lt;p&gt;The current audio bridge passes changes to an AudioWorklet. The worklet renders drums, bass and rhythm accompaniment and handles their timing away from interface rendering. Its live schedule can also feed WebMIDI output through the bridge.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/AudioWorklet" rel="noopener noreferrer"&gt;MDN's AudioWorklet reference&lt;/a&gt; explains the separate Web Audio execution context. That separation helps explain this architecture, but the use of a worklet alone does not establish that a particular application has no timing problems.&lt;/p&gt;

&lt;p&gt;The interface has to respond to a request. The audio engine has to decide when an allowed change takes effect. In this system, a playing change can queue for a beat or bar boundary; a stopped change can apply immediately.&lt;/p&gt;

&lt;p&gt;Consider a tempo adjustment requested halfway through a bar. The player has made a valid request, but applying it at an arbitrary instant may disrupt the phrase. Queuing it gives the music a meaningful point at which to change. This is a design choice about musical behavior, with implementation consequences for state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Distinguish selected pending and applied state
&lt;/h2&gt;

&lt;p&gt;A delayed musical change creates more than one truthful description of the current situation.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;th&gt;What the player needs to understand&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Selected&lt;/td&gt;
&lt;td&gt;The player has requested a new value.&lt;/td&gt;
&lt;td&gt;The intended next setting.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pending&lt;/td&gt;
&lt;td&gt;The change is waiting for a musical boundary.&lt;/td&gt;
&lt;td&gt;Why the current sound may still reflect the previous value.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Applied&lt;/td&gt;
&lt;td&gt;The audio engine has adopted the change.&lt;/td&gt;
&lt;td&gt;Which setting is active in the music.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are design distinctions, not three competing copies of the truth. A selected value can be correct as an intention while the old value remains correct as the applied sound.&lt;/p&gt;

&lt;p&gt;For a hypothetical example, imagine changing from one tempo to another during playback with a bar-boundary change pending. If the interface presents the new value as already active, a player may think the engine ignored it. If it hides the request until the boundary, the player may repeat it. The product needs to communicate the relationship between intention and execution.&lt;/p&gt;

&lt;p&gt;The interface needs to show that the request was accepted and when it takes effect. During playback, that difference is audible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let the physical switch correct the requirement
&lt;/h2&gt;

&lt;p&gt;During early development, I tested a purchased Bluetooth footswitch and found that its behavior was a toggle. The interaction had assumed a hold-to-talk gesture.&lt;/p&gt;

&lt;p&gt;That was not a problem I could resolve by looking only at a button handler. The intended gesture and the actual device disagreed. I fed the finding back into Cursor and refined the interaction around a tap that starts listening while recognition is awaited. Getting it usable required debugging and repeated manual testing.&lt;/p&gt;

&lt;p&gt;Scene recall with the footswitch was a satisfying early result for me. That is a historical acceptance observation, not certification of the current version or every controller.&lt;/p&gt;

&lt;p&gt;The important requirement came from trying the hardware in the activity. With both hands on a guitar, a switch has to support a gesture the player can actually perform and recognize. A software abstraction can describe a press cleanly while missing how the physical control behaves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep local scenes and validation understandable
&lt;/h2&gt;

&lt;p&gt;The current product provides ten local scenes, with JSON export and import for backup or moving configurations between browsers. Keeping musical state in the browser avoids an account and cloud setup step. It also makes the storage boundary part of the experience: a scene saved in one browser is not automatically present in another.&lt;/p&gt;

&lt;p&gt;Local state should not be confused with offline support for every feature. Browser speech recognition has its own capabilities and dependencies. A local scene system does not make a browser's recognition service work offline.&lt;/p&gt;

&lt;p&gt;Validation has similar boundaries. Agents handled automated checks. I supplied manual acceptance testing and merge decisions. Those activities can support different claims, and none should stand in for all the others.&lt;/p&gt;

&lt;p&gt;I reported a successful live MIDI output test through an IAC bus into Logic Pro on September 29, 2026. That is useful evidence for that route. It does not establish behavior with every physical receiver or controller. An interface screenshot can establish a visible control or layout, but cannot establish that the music sounds right.&lt;/p&gt;

&lt;p&gt;The next useful checks are therefore about the actual interaction: can a player request a change without losing the phrase, understand when it takes effect and recover when an optional input method is unavailable? The current version has not yet been part of my regular practice beyond QA. Those practice questions remain work to do.&lt;/p&gt;

&lt;p&gt;Building a browser music tool made the input and timing boundary unusually tangible for me. The system must accept the player's intention, preserve a musically sensible transition and communicate the difference. Real hardware then tests whether that intention was represented correctly in the first place.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;This article was drafted and edited with AI assistance, using my original project account and the current case study.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>ux</category>
    </item>
  </channel>
</rss>
