<?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: Colux</title>
    <description>The latest articles on DEV Community by Colux (@c01ux).</description>
    <link>https://dev.to/c01ux</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1216902%2F944a9602-cd7d-4151-9656-a0a121905dab.jpg</url>
      <title>DEV Community: Colux</title>
      <link>https://dev.to/c01ux</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/c01ux"/>
    <language>en</language>
    <item>
      <title>Building LibreTune: A Deep Dive into Modern Android Music App Development</title>
      <dc:creator>Colux</dc:creator>
      <pubDate>Mon, 15 Sep 2025 16:03:24 +0000</pubDate>
      <link>https://dev.to/c01ux/building-libretune-a-deep-dive-into-modern-android-music-app-development-26bn</link>
      <guid>https://dev.to/c01ux/building-libretune-a-deep-dive-into-modern-android-music-app-development-26bn</guid>
      <description>&lt;p&gt;&lt;strong&gt;LibreTune&lt;/strong&gt;. The name itself suggests freedom and melody. For me, it represents a journey into the intricate world of modern Android app development, a personal quest to build a music player that not only works but thrives on reactive principles, sophisticated data handling, and a seamless user experience. &lt;/p&gt;

&lt;p&gt;This isn't just another music app; it's a testament to learning, overcoming challenges, and embracing the power of &lt;em&gt;Jetpack Compose&lt;/em&gt; and &lt;em&gt;Kotlin Flow&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Genesis: Why LibreTune?
&lt;/h3&gt;

&lt;p&gt;Every project starts with an idea, often born from curiosity or a personal itch. For me, it was both. I wanted to explore how to build a robust media application from the ground up, integrating complex data flows, a local-first caching strategy, and a highly dynamic UI. &lt;/p&gt;

&lt;p&gt;While existing music apps are plentiful, the opportunity to architect one's own, understanding every layer from the UI down to the network requests, was too compelling to pass up. &lt;strong&gt;LibreTune&lt;/strong&gt; became my playground for mastering the nuances of a truly reactive Android ecosystem.&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%2Fgbn4wiqz1erik8wcu588.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%2Fgbn4wiqz1erik8wcu588.png" alt=" " width="512" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Architectural Foundations: Embracing Clean Code and MVI
&lt;/h3&gt;

&lt;p&gt;From the outset, a solid architecture was paramount. I opted for a combination of &lt;strong&gt;Clean Architecture&lt;/strong&gt; principles and the &lt;strong&gt;Model-View-Intent (MVI)&lt;/strong&gt; pattern. This meant clear separation of concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UI Layer (Compose):&lt;/strong&gt; Responsible for rendering the UI and reacting to user interactions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ViewModel Layer:&lt;/strong&gt; Manages UI state, handles user intents, and orchestrates data from the repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain Layer:&lt;/strong&gt; Contains business logic and use cases, agnostic to UI or data sources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Layer (Repository, DAOs, Network):&lt;/strong&gt; Handles all data operations, abstracting whether data comes from a local database, a network API, or an in-memory cache.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The MVI pattern, with its single source of truth for UI state (a &lt;code&gt;StateFlow&lt;/code&gt; in the ViewModel), proved invaluable. It simplifies state management and makes the UI highly predictable. Each user interaction translates into an "Intent," which the ViewModel processes, leading to a new "State" that the UI then observes and renders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning &amp;amp; Challenge:&lt;/strong&gt; Initially, differentiating between the responsibilities of the Domain and Data layers was a nuanced challenge. When should a "use case" live in the Domain layer versus a direct call from the ViewModel to the Repository? The learning here was to prioritize clarity: if logic involves more than just fetching and mapping data (e.g., combining multiple repository calls, applying business rules), it belongs in a Use Case. For simple fetch-and-transform operations, the Repository suffices. This decision-making process refined my understanding of true separation of concerns.&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%2F2x2tdlxe4pyfau5kpi3h.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%2F2x2tdlxe4pyfau5kpi3h.png" alt=" " width="800" height="1777"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Reactive Heartbeat: Kotlin Flow and State Management
&lt;/h3&gt;

&lt;p&gt;Kotlin Flow is the lifeblood of LibreTune. Every piece of data, from search results to playback history and home screen recommendations, flows through &lt;code&gt;Flow&lt;/code&gt;s and is exposed as &lt;code&gt;StateFlow&lt;/code&gt;s or &lt;code&gt;SharedFlow&lt;/code&gt;s. This reactive paradigm ensures that the UI always reflects the latest available data with minimal boilerplate.&lt;/p&gt;

&lt;h4&gt;
  
  
  Dynamic Home Screen Feeds: A Symphony of &lt;code&gt;combine&lt;/code&gt; and &lt;code&gt;flatMapLatest&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;One of the most complex, yet rewarding, challenges was building the dynamic home screen. Imagine a home screen that learns from your behavior: "Play It Again" from your recent history, "More from [Artist]" based on artists you've saved, and "Fans Also Like" from related artists. This requires merging multiple data streams:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;HistoryDao&lt;/code&gt;:&lt;/strong&gt; For recently played songs, artists, and albums.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;LibraryDao&lt;/code&gt;:&lt;/strong&gt; For saved artists and playlists.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;ArtistDao&lt;/code&gt; &amp;amp; &lt;code&gt;PlaylistDao&lt;/code&gt;:&lt;/strong&gt; To fetch related content based on the "seed" data from history/library.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The solution involved a powerful combination of &lt;code&gt;Flow&lt;/code&gt; operators:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;combine&lt;/code&gt;:&lt;/strong&gt; Used extensively to merge multiple &lt;code&gt;Flow&lt;/code&gt;s into a single &lt;code&gt;Flow&lt;/code&gt;. For instance, combining &lt;code&gt;recentlyPlayedArtistsFlow&lt;/code&gt; and &lt;code&gt;savedArtistsFlow&lt;/code&gt; to create a unified &lt;code&gt;seedArtistsFlow&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;flatMapLatest&lt;/code&gt;:&lt;/strong&gt; Crucial for creating dependent flows. Once we had our &lt;code&gt;seedArtists&lt;/code&gt; and &lt;code&gt;seedAlbums&lt;/code&gt;, &lt;code&gt;flatMapLatest&lt;/code&gt; allowed us to launch &lt;em&gt;new&lt;/em&gt; flows (e.g., &lt;code&gt;getAlbumsAndSinglesByArtistId&lt;/code&gt; for each seed artist) and dynamically re-collect them whenever the seed data changed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; My initial attempt to build this feed suffered from a critical bug: calling &lt;code&gt;.collect()&lt;/code&gt; inside the &lt;code&gt;combine&lt;/code&gt; operator's transform block. This caused the entire flow to hang indefinitely. The &lt;code&gt;collect&lt;/code&gt; function is suspending and blocking, designed for terminal operations, not for transforming data within a reactive stream.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning:&lt;/strong&gt; This forced me to truly understand the difference between a suspending, blocking operation and a declarative transformation. The fix involved ensuring every step in the data pipeline was a &lt;code&gt;Flow&lt;/code&gt; operator (&lt;code&gt;map&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;flatMapLatest&lt;/code&gt;, &lt;code&gt;combine&lt;/code&gt;) and that &lt;code&gt;.collect()&lt;/code&gt; was only called once at the very end in the ViewModel. This experience solidified my understanding of how to compose complex reactive data streams without inadvertently blocking them.&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%2F26enymhjk6q7700c4w0m.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%2F26enymhjk6q7700c4w0m.png" alt=" " width="800" height="1777"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Smart Throttling: Optimizing UI Updates
&lt;/h4&gt;

&lt;p&gt;Another subtle, yet impactful, learning came from optimizing how frequently the UI updates from rapidly changing data sources. For example, a music player's progress bar or a continuously updating recommendation feed. Updating the UI on every single data change can lead to unnecessary recompositions and battery drain.&lt;/p&gt;

&lt;p&gt;I implemented a custom &lt;code&gt;Flow&lt;/code&gt; operator called &lt;code&gt;smartThrottle&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Flow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;smartThrottle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;emitNowPredicate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;previous&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Flow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;lastEmitTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0L&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;previousItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;currentItem&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;currentTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;shouldEmit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="nf"&gt;emitNowPredicate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;previousItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;currentItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="c1"&gt;// Emit if predicate is true&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentTime&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;lastEmitTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inWholeMilliseconds&lt;/span&gt; &lt;span class="c1"&gt;// Emit if time is up&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;shouldEmit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;lastEmitTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentTime&lt;/span&gt;
            &lt;span class="n"&gt;previousItem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentItem&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;This operator allows for intelligent updates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;First emission is always instant:&lt;/strong&gt; Ensures a fast initial load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Critical changes are instant:&lt;/strong&gt; A &lt;code&gt;predicate&lt;/code&gt; function allows me to define conditions (e.g., song changed, play/pause state toggled) that trigger an immediate update.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-critical changes are throttled:&lt;/strong&gt; Updates like playback position (if only the position changes) are emitted only after a specified duration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; The initial &lt;code&gt;sample&lt;/code&gt; operator was too aggressive; it delayed even the first emission. Then, the first iteration of &lt;code&gt;smartThrottle&lt;/code&gt; didn't consider the &lt;code&gt;previousItem&lt;/code&gt;, making it impossible to check for &lt;em&gt;changes&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning:&lt;/strong&gt; This iterative refinement highlighted the importance of fine-grained control over Flow emissions and the power of &lt;code&gt;transform&lt;/code&gt; for building highly custom operators. It also taught me to anticipate performance bottlenecks and proactively design solutions to mitigate them, ensuring a smooth and responsive user experience even with high-frequency data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Jetpack Compose: Building a Dynamic and Responsive UI
&lt;/h3&gt;

&lt;p&gt;Jetpack Compose has been a game-changer for LibreTune's UI. Its declarative nature aligns perfectly with reactive data flows.&lt;/p&gt;

&lt;h4&gt;
  
  
  From Scaffold to Shared Composable: The &lt;code&gt;ScaffoldWithDrawer&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;To maintain consistency and reduce boilerplate, I extracted the common screen structure (drawer navigation, &lt;code&gt;Scaffold&lt;/code&gt;, &lt;code&gt;TopAppBar&lt;/code&gt;, &lt;code&gt;BackHandler&lt;/code&gt;) into a reusable &lt;code&gt;ScaffoldWithDrawer&lt;/code&gt; composable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;ScaffoldWithDrawer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;navController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;NavHostController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;topBar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@Composable&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;openDrawer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;@Composable&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paddingValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PaddingValues&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... drawerState, BackHandler, ModalNavigationDrawer ...&lt;/span&gt;
    &lt;span class="nc"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topBar&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;topBar&lt;/span&gt;&lt;span class="p"&gt;(&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="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;innerPadding&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="nf"&gt;content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;innerPadding&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;&lt;strong&gt;Challenge:&lt;/strong&gt; My initial &lt;code&gt;BackHandler&lt;/code&gt; implementation within the &lt;code&gt;ScaffoldWithDrawer&lt;/code&gt; was occasionally overridden by &lt;code&gt;BackHandler&lt;/code&gt;s in child composables, leading to unexpected behavior (e.g., pressing back wouldn't close the drawer if a search bar was focused).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning:&lt;/strong&gt; This taught me the "nearest &lt;code&gt;BackHandler&lt;/code&gt; wins" rule in Compose. The solution was to carefully manage the &lt;code&gt;enabled&lt;/code&gt; state of &lt;code&gt;BackHandler&lt;/code&gt;s, ensuring that more specific handlers (like clearing search focus) take precedence when active, while the general drawer-closing handler remains the fallback.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Pitfalls of Nested Scrolling
&lt;/h4&gt;

&lt;p&gt;A classic Compose pitfall I encountered was attempting to nest two vertically scrolling components (e.g., a &lt;code&gt;LazyColumn&lt;/code&gt; inside another &lt;code&gt;LazyColumn&lt;/code&gt;). This predictably resulted in an &lt;code&gt;IllegalStateException&lt;/code&gt; due to ambiguous height measurements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning:&lt;/strong&gt; The fundamental rule is: &lt;strong&gt;one scrolling container per direction.&lt;/strong&gt; If the entire screen needs to scroll, use a single &lt;code&gt;LazyColumn&lt;/code&gt; for the root, and define different &lt;code&gt;item { ... }&lt;/code&gt; blocks for various content sections (headers, carousels, lists). &lt;code&gt;Modifier.fillParentMaxHeight()&lt;/code&gt; is a lifesaver within a &lt;code&gt;LazyColumn&lt;/code&gt;'s &lt;code&gt;item&lt;/code&gt; block when a child needs to take up the remaining screen height.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimizing Media Playback: ExoPlayer and the Service Layer
&lt;/h3&gt;

&lt;p&gt;Integrating &lt;code&gt;ExoPlayer&lt;/code&gt; into a &lt;code&gt;MediaSessionService&lt;/code&gt; was a significant undertaking. The goal was smooth, background playback with robust controls and notifications.&lt;/p&gt;

&lt;h4&gt;
  
  
  Custom Commands and Asynchronous URL Fetching
&lt;/h4&gt;

&lt;p&gt;A key performance challenge was handling song playback when the media URL wasn't immediately available. Directly setting &lt;code&gt;MediaItems&lt;/code&gt; with remote URLs can cause delays. The solution involved:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Custom &lt;code&gt;SessionCommand&lt;/code&gt;:&lt;/strong&gt; To tell the &lt;code&gt;PlaybackService&lt;/code&gt; to load a playlist and a starting index.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Placeholder &lt;code&gt;MediaItems&lt;/code&gt;:&lt;/strong&gt; Initially adding &lt;code&gt;MediaItems&lt;/code&gt; with just a &lt;code&gt;mediaId&lt;/code&gt; (the song ID) but no actual &lt;code&gt;Uri&lt;/code&gt; to &lt;code&gt;ExoPlayer&lt;/code&gt;. This immediately updates the playlist structure.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Background URL Fetching:&lt;/strong&gt; Using a &lt;code&gt;CoroutineScope&lt;/code&gt; in the service to fetch the actual URL for the &lt;em&gt;currently playing&lt;/em&gt; song, replace its &lt;code&gt;MediaItem&lt;/code&gt;, prepare the player, and start playback.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Pre-fetching:&lt;/strong&gt; Continuously fetching URLs for the &lt;em&gt;rest&lt;/em&gt; of the playlist in the background, asynchronously replacing their &lt;code&gt;MediaItems&lt;/code&gt; as URLs become available. This ensures a smooth transition to the next song.&lt;/li&gt;
&lt;/ol&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified snippet from PlaybackService&lt;/span&gt;
&lt;span class="n"&gt;backgroundFetchJob&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoroutineScope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Dispatchers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;songUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getOrFetchUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;startingSong&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Fetches from cache or network&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;songUrl&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;realMediaItem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;placeholderMediaItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;startingIndex&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;buildUpon&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;setUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;songUrl&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;exoPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceMediaItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;startingIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;realMediaItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;exoPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;exoPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ... continue pre-fetching others ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; Initially, the player would pause and buffer visibly when transitioning to a song whose URL hadn't been fetched yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning:&lt;/strong&gt; This led to a deeper understanding of &lt;code&gt;ExoPlayer&lt;/code&gt;'s &lt;code&gt;replaceMediaItem&lt;/code&gt; and the importance of background pre-fetching. It demonstrated how to decouple the UI's perception of a playlist (placeholder items) from the actual readiness of the media content (fetched URLs), significantly improving the playback experience.&lt;/p&gt;

&lt;h4&gt;
  
  
  In-Memory URL Caching
&lt;/h4&gt;

&lt;p&gt;To further boost performance, especially for repeated plays of the same song within a session, I implemented a simple in-memory cache (&lt;code&gt;MutableMap&amp;lt;String, String&amp;gt;&lt;/code&gt;) in the &lt;code&gt;PlaybackService&lt;/code&gt;. Before making a network request for a song's URL, the service first checks this cache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; The main challenge with URL caching is knowing when URLs expire.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learning:&lt;/strong&gt; For a single-session optimization, a simple &lt;code&gt;Map&lt;/code&gt; is sufficient. For persistent caching across sessions, a more robust solution with a Time-To-Live (TTL) mechanism would be necessary, constantly re-fetching expired URLs. This pragmatic approach allowed for a significant performance gain without over-engineering for a feature beyond the current scope.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next for LibreTune?
&lt;/h3&gt;

&lt;p&gt;LibreTune, while a powerful learning tool, is still evolving. Future plans include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Offline Mode:&lt;/strong&gt; Allowing users to download and play songs offline. This would involve robust local storage and playback logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Authentication:&lt;/strong&gt; For personalized settings and cloud sync.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More Recommendation Algorithms:&lt;/strong&gt; Leveraging more advanced patterns from user behavior to suggest even more relevant content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility Enhancements:&lt;/strong&gt; Ensuring the app is usable by everyone.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion: The Journey Continues
&lt;/h3&gt;

&lt;p&gt;Building LibreTune has been an incredibly enriching experience. It pushed me to master Kotlin Flow's intricacies, leverage Jetpack Compose's declarative power, and design a resilient media architecture. Each challenge was a learning opportunity, refining my skills and deepening my appreciation for modern Android development.&lt;/p&gt;

&lt;p&gt;This project stands as a testament to the idea that sometimes, the best way to learn is to build, iterate, and solve problems head-on. The melody of code continues, and so does the journey of LibreTune.&lt;/p&gt;

&lt;p&gt;Have a look at the &lt;a href="https://github.com/c01ux/libre-tune" rel="noopener noreferrer"&gt;source code on GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>android</category>
      <category>music</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Liberty Beats - Build a DAW in React</title>
      <dc:creator>Colux</dc:creator>
      <pubDate>Mon, 01 Jul 2024 06:45:02 +0000</pubDate>
      <link>https://dev.to/c01ux/liberty-beats-build-a-daw-in-react-1l89</link>
      <guid>https://dev.to/c01ux/liberty-beats-build-a-daw-in-react-1l89</guid>
      <description>

&lt;p&gt;In this blog post, we will explore how to build a Digital Audio Workstation (DAW) in React. We will use the Tone.js library to create music and audio effects, and we will discuss the key components and features of a DAW application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;A Digital Audio Workstation (DAW) is a software application used for recording, editing, and producing audio files. DAWs are commonly used by musicians, sound engineers, and producers to create music, podcasts, and other audio content.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will dive into the logic of &lt;strong&gt;Liberty Beats&lt;/strong&gt;, a simple (but powerful) DAW application in React that allows users to create and edit music tracks.&lt;/p&gt;

&lt;p&gt;The application is built using React components, state management, and the &lt;a href="https://tonejs.github.io/" rel="noopener noreferrer"&gt;Tone.js&lt;/a&gt; library for audio synthesis and effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  First Steps
&lt;/h2&gt;

&lt;p&gt;Before we start building the DAW application, let's outline the key features and components that we want to include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Track Editor&lt;/strong&gt;: A visual interface for creating and editing music tracks. Users can add, remove, and modify audio clips, adjust volume and panning, and apply effects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Transport Controls&lt;/strong&gt;: Play, pause, stop, and seek controls for playback. Users can control the tempo from the panel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mixer&lt;/strong&gt;: A mixer section for each track, where users can adjust volume, set solo and mute options.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create a Vite React App and Set Up Tone.js
&lt;/h2&gt;

&lt;p&gt;To get started, we will create a new React application using &lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt;, a fast build tool that supports React and modern JavaScript features.&lt;/p&gt;

&lt;p&gt;Then, follow my previous blog post on how to &lt;a href="https://blog.coluzziandrea.com/react-tonejs" rel="noopener noreferrer"&gt;integrate Tone.js in a React application&lt;/a&gt; to set up Tone.js in your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a store for the DAW
&lt;/h2&gt;

&lt;p&gt;In Liberty Beats, we used Redux Toolkit to manage the application state. &lt;a href="https://redux-toolkit.js.org/" rel="noopener noreferrer"&gt;Redux Toolkit&lt;/a&gt; is a powerful library that simplifies the process of managing state in React applications.&lt;/p&gt;

&lt;p&gt;Once you have set up Redux Toolkit in your project, you can create a store for the DAW application. The store will hold the state of the application, and the most important part is the playlist slice, which will contain the tracks and their settings.&lt;/p&gt;

&lt;p&gt;Here's the type of the track object, taken from the &lt;a href="https://github.com/coluzziandrea/liberty-beats" rel="noopener noreferrer"&gt;Liberty Beats&lt;/a&gt; project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Track&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * Tailwind CSS color class
   * @example 'green'
   */&lt;/span&gt;
  &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TrackColor&lt;/span&gt;
  &lt;span class="nx"&gt;instrumentPreset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InstrumentPreset&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * Drums track specific data (can be undefined if track is not drums track)
   */&lt;/span&gt;
  &lt;span class="nx"&gt;trackDrums&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TrackDrums&lt;/span&gt;
  &lt;span class="nx"&gt;bars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="nx"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;

  &lt;span class="nx"&gt;muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="nx"&gt;soloed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="nx"&gt;areThereAnyOtherTrackSoloed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;Track&lt;/code&gt; interface, we define the properties of a track:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt;: A unique identifier for the track.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;title&lt;/code&gt;: The name of the track.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;color&lt;/code&gt;: A Tailwind CSS color class for the track (values set in the &lt;code&gt;TrackColor&lt;/code&gt; enum).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;instrumentPreset&lt;/code&gt;: The instrument preset for the track, defined in the &lt;code&gt;InstrumentPreset&lt;/code&gt; enum.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;trackDrums&lt;/code&gt;: Specific data for drums tracks (can be &lt;code&gt;undefined&lt;/code&gt; if the track is not a drums track). Drums need a different representation in the UI, and also different settings when it comes to the sequencer.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bars&lt;/code&gt;: An array of &lt;code&gt;Bar&lt;/code&gt; objects, representing the bars in the track. Each &lt;code&gt;Bar&lt;/code&gt; object contains the notes for each step in the bar.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;volume&lt;/code&gt;: The volume level of the track.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;muted&lt;/code&gt;: A boolean value indicating whether the track is muted.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;soloed&lt;/code&gt;: A boolean value indicating whether the track is soloed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;areThereAnyOtherTrackSoloed&lt;/code&gt;: A boolean value indicating whether there are other tracks soloed in the playlist.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building the Editor
&lt;/h2&gt;

&lt;p&gt;Building the Editor is one of the most challenging parts of the DAW application. The Editor is a grid-based interface where users can create and edit music patterns by placing notes on a grid.&lt;/p&gt;

&lt;p&gt;In Liberty Beats, we used a custom Editor component that allows users to create and edit music patterns for each track. The Editor component is built using React and Tone.js, and it provides a visual interface for creating and editing music patterns.&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%2Fesseoto314kd36tkexv5.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%2Fesseoto314kd36tkexv5.png" alt="sequencer" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the very beginning of the project, the Grid was made using a simple HTML table, but then I switched to a more performant solution using a canvas element. The canvas element allows for better performance and more flexibility when it comes to drawing the grid and the notes.&lt;/p&gt;

&lt;p&gt;Here's a simplified version of the Editor component in Liberty Beats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Note&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../types&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;drawGrid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;drawNotes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../utils/draw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;EditorProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;bars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Bar&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="nx"&gt;onNoteClick&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;barIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stepIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Editor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EditorProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;bars&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onNoteClick&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvasRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLCanvasElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nx"&gt;canvasRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvasRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;drawGrid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;drawNotes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bars&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="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bars&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MouseEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLCanvasElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stepIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;barIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;onNoteClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;barIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stepIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;
      &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;canvasRef&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;bars&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="nx"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;bars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Editor&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing the Transport layer
&lt;/h2&gt;

&lt;p&gt;The Transport layer is responsible for controlling the playback of the tracks in the DAW application. It provides controls for playing, pausing, stopping, and seeking the playback of the tracks.&lt;/p&gt;

&lt;p&gt;In Liberty Beats, we used the Tone.Transport object from the Tone.js library to control the playback of the tracks. The Transport layer provides controls for starting, stopping, and seeking the playback of the tracks, as well as setting the tempo and time signature.&lt;/p&gt;

&lt;p&gt;Using the store as a source of truth, we can easily control the playback of the tracks in the application. Here's a simplified version of the Transport component in Liberty Beats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useDispatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useSelector&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;selectPlaylist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPlaying&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../store/playlistSlice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDispatch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;playlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectPlaylist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handlePlay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setPlaying&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handlePause&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setPlaying&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleStop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setPlaying&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handlePlay&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Play&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handlePause&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Pause&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleStop&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Stop&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Transport&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog post, we explored how to build a Digital Audio Workstation (DAW) in React. We discussed the key components and features of a DAW application, including the Track Editor and Transport Control.&lt;/p&gt;

&lt;p&gt;We used the Tone.js library to create music and audio effects, and we discussed how to manage the state of the application using Redux Toolkit.&lt;/p&gt;

&lt;p&gt;Liberty Beats is a simple (but powerful) DAW application that allows users to create and edit music tracks. The application is built using React components, state management, and the Tone.js library for audio synthesis and effects.&lt;/p&gt;

&lt;p&gt;You can find the full source code of the Liberty Beats project on GitHub: &lt;a href="https://github.com/coluzziandrea/liberty-beats" rel="noopener noreferrer"&gt;Liberty Beats&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this tutorial and found it helpful. If you have any questions or feedback, feel free to leave a comment below.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>music</category>
    </item>
    <item>
      <title>Integrate Tone.js in a React application</title>
      <dc:creator>Colux</dc:creator>
      <pubDate>Sat, 13 Apr 2024 10:24:43 +0000</pubDate>
      <link>https://dev.to/c01ux/integrate-tonejs-in-a-react-application-319d</link>
      <guid>https://dev.to/c01ux/integrate-tonejs-in-a-react-application-319d</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;How to use Tone.js library to create music in a React application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The tone.js library is a popular choice for creating interactive audio applications in the browser. It provides a high-level abstraction layer over the native Web Audio API, making it easier to work with audio synthesis, effects, and sequencing. Tone.js offers a rich set of features, including oscillators, filters, effects, and samplers, allowing developers to create complex soundscapes and music compositions with ease. In this blog post, we will explore how to integrate Tone.js into a React application and use it to create music.&lt;/p&gt;

&lt;p&gt;We will also discuss the pros and cons of using Tone.js for web audio development, helping you decide whether it is the right choice for your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Tone.js Works
&lt;/h2&gt;

&lt;p&gt;Tone.js is built on top of the Web Audio API, a powerful browser feature that allows developers to create and manipulate audio in the browser. The library provides a set of classes and functions that simplify common audio tasks, such as creating oscillators, applying effects, and sequencing notes.&lt;/p&gt;

&lt;p&gt;Here's a simple example of how to create a basic synthesizer using Tone.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as Tone from 'tone'

// Create a new synthesizer
const synth = new Tone.Synth().toDestination()

// Play a note
synth.triggerAttackRelease('C4', '8n')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we import the Tone.js library and create a new synthesizer using the &lt;code&gt;Tone.Synth&lt;/code&gt; class. We then play a note by calling the &lt;code&gt;triggerAttackRelease&lt;/code&gt; method with the note name ('C4') and duration ('8n') as arguments.&lt;/p&gt;

&lt;p&gt;Tone.js provides a wide range of classes and functions for creating more complex audio applications, including oscillators, filters, effects, and sequencers. Developers can combine these components to build interactive music compositions, games, and other audio-driven experiences in the browser.&lt;/p&gt;

&lt;p&gt;For more information on Tone.js and its features, you can refer to the &lt;a href="https://tonejs.github.io/" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating Tone.js in a React Applications
&lt;/h2&gt;

&lt;p&gt;Integrating Tone.js into a React project introduces some unique technical considerations, given React's component-based architecture and lifecycle methods. Let's explore some key technical aspects of using Tone.js in a web project built with React.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Managing Application Lifecycle
&lt;/h3&gt;

&lt;p&gt;In React, managing state and component lifecycle is crucial for building interactive applications. When incorporating Tone.js, developers should consider how audio state interacts with React component state and lifecycle methods.&lt;/p&gt;

&lt;p&gt;One common approach is to use React's state management to control audio playback, effects, and other parameters. Developers can leverage React's &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useEffect&lt;/code&gt; hooks to update audio state and trigger side effects when components mount, update, or unmount.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState, useEffect } from 'react'
import * as Tone from 'tone'

const AudioPlayer = () =&amp;gt; {
  const [isPlaying, setIsPlaying] = useState(false)
  const [synth, setSynth] = useState(null)

  useEffect(() =&amp;gt; {
    // Initialize Tone.js components
    const newSynth = new Tone.Synth().toDestination()
    setSynth(newSynth)

    return () =&amp;gt; {
      // Clean up Tone.js components
      newSynth.dispose()
    }
  }, [])

  const handlePlay = () =&amp;gt; {
    if (synth) {
      synth.triggerAttackRelease('C4', '8n')
      setIsPlaying(true)
    }
  }

  const handleStop = () =&amp;gt; {
    if (synth) {
      synth.triggerRelease()
      setIsPlaying(false)
    }
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;button onClick={handlePlay} disabled={isPlaying}&amp;gt;
        Play
      &amp;lt;/button&amp;gt;
      &amp;lt;button onClick={handleStop} disabled={!isPlaying}&amp;gt;
        Stop
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export default AudioPlayer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we were using a state management library like &lt;code&gt;Redux&lt;/code&gt; or &lt;code&gt;Context API&lt;/code&gt;, we could also manage audio state globally and share it across components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Optimizing Performance
&lt;/h3&gt;

&lt;p&gt;Efficiently managing performance is essential when using Tone.js in React, especially for applications with complex audio processing or large component trees. Developers should be mindful of potential performance bottlenecks and optimize where necessary.&lt;/p&gt;

&lt;p&gt;One optimization technique is to use memoization with React's useMemo hook to cache expensive computations or objects, such as Tone.js synthesizers or effects, preventing unnecessary re-renders and memory allocations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState, useEffect, useMemo } from 'react'
import * as Tone from 'tone'

const AudioPlayer = () =&amp;gt; {
  const [isPlaying, setIsPlaying] = useState(false)

  const synth = useMemo(() =&amp;gt; new Tone.Synth().toDestination(), [])

  useEffect(() =&amp;gt; {
    return () =&amp;gt; {
      synth.dispose()
    }
  }, [synth])

  const handlePlay = () =&amp;gt; {
    synth.triggerAttackRelease('C4', '8n')
    setIsPlaying(true)
  }

  const handleStop = () =&amp;gt; {
    synth.triggerRelease()
    setIsPlaying(false)
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;button onClick={handlePlay} disabled={isPlaying}&amp;gt;
        Play
      &amp;lt;/button&amp;gt;
      &amp;lt;button onClick={handleStop} disabled={!isPlaying}&amp;gt;
        Stop
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export default AudioPlayer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pros of Using Tone.js
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;### Abstraction Layer&lt;/p&gt;

&lt;p&gt;Tone.js provides a high-level abstraction layer over the native Web Audio API, simplifying complex audio operations. Developers can focus on creating musical compositions and interactions without getting bogged down in low-level audio programming details.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;### Comprehensive Feature Set&lt;/p&gt;

&lt;p&gt;The library offers a rich set of features, including oscillators, effects, filters, and samplers, enabling developers to create intricate soundscapes and music compositions with ease.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;### Cross-Browser Compatibility&lt;/p&gt;

&lt;p&gt;Tone.js handles browser inconsistencies and optimizations under the hood, ensuring consistent audio playback across different platforms and browsers. This saves developers from the hassle of dealing with compatibility issues themselves.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;### Community and Documentation&lt;/p&gt;

&lt;p&gt;Tone.js benefits from an active community and extensive documentation, making it easy for developers to get started and find solutions to common challenges. The library is well-maintained and frequently updated with new features and improvements.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;### Integration with Web Audio Worklet&lt;/p&gt;

&lt;p&gt;Tone.js seamlessly integrates with the Web Audio Worklet API, allowing for efficient audio processing in separate threads. This enables developers to build performant and responsive web audio applications.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Cons of Using Tone.js
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;### Additional Dependency&lt;/p&gt;

&lt;p&gt;Using Tone.js adds an extra dependency to your project, which may increase the overall bundle size and complexity. Developers should consider whether the benefits outweigh the cost of including an additional library in their application.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;### Learning Curve&lt;/p&gt;

&lt;p&gt;While Tone.js simplifies many aspects of web audio programming, it still requires a learning curve, especially for developers new to audio synthesis and processing concepts. Beginners may need to invest time in understanding the library's API and best practices.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;### Performance Overhead&lt;/p&gt;

&lt;p&gt;Although Tone.js aims to optimize audio performance, it may introduce some performance overhead compared to using native Web Audio API directly. Developers building performance-critical applications should carefully assess whether Tone.js meets their performance requirements.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;### Limited Low-Level Control&lt;/p&gt;

&lt;p&gt;Tone.js abstracts away many low-level audio details, which can be advantageous for rapid development but may limit the level of control for advanced users. Developers with specific audio processing requirements may find themselves constrained by the library's high-level abstractions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;### Browser Support for Advanced Features&lt;/p&gt;

&lt;p&gt;Some advanced features of Tone.js, such as Web Audio Worklet integration, may not be fully supported in all browsers, requiring fallback mechanisms or alternative approaches for cross-browser compatibility.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Tone.js offers a powerful and convenient way to incorporate interactive audio into web applications, with its abstraction layer, comprehensive feature set, and community support. However, developers should weigh the pros and cons carefully to determine whether it aligns with their project requirements and constraints. For those seeking a balance between ease of use and performance, Tone.js remains a compelling choice in the landscape of web audio development.&lt;/p&gt;

&lt;p&gt;I hope you found this blog post helpful and that you learned something new. If you have any questions or feedback, feel free to reach out to me on &lt;a href="https://twitter.com/andreacoluzzi94" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. I'm always happy to help!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>audio</category>
    </item>
    <item>
      <title>Drag and Drop feature in React</title>
      <dc:creator>Colux</dc:creator>
      <pubDate>Wed, 20 Mar 2024 18:06:06 +0000</pubDate>
      <link>https://dev.to/c01ux/drag-and-drop-feature-in-react-jpj</link>
      <guid>https://dev.to/c01ux/drag-and-drop-feature-in-react-jpj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;How to Implement Drag and Drop feature without using any library.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this blog post, I will show you how to implement a simple Drag and Drop feature in a React application without using any library.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Drag and Drop?
&lt;/h3&gt;

&lt;p&gt;The Drag and Drop feature is a common interaction pattern that allows users to move elements around the page. It is a great way to make your application more interactive and user-friendly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;To implement the Drag and Drop feature, we will use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API" rel="noopener noreferrer"&gt;HTML5 Drag and Drop API&lt;/a&gt;. This API provides a set of events and methods that allow you to create a custom Drag and Drop behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a new React application
&lt;/h3&gt;

&lt;p&gt;First, let's create a new React application using Create React App.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app drag-n-drop --template typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And let's navigate to the newly created project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd drag-n-drop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can start the development server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will start the development server and open the application in your default web browser.&lt;/p&gt;

&lt;p&gt;Now that we have our React application set up, let's move on to the next step. But before, let's open our project in our favorite code editor. I personally use Visual Studio Code, but you can use any code editor you prefer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;code .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create a Draggable component
&lt;/h3&gt;

&lt;p&gt;Next, let's create a new component called &lt;code&gt;Draggable&lt;/code&gt; that will represent the draggable element.&lt;/p&gt;

&lt;p&gt;We'll create it in the &lt;code&gt;src&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react'

const Draggable: React.FC = () =&amp;gt; {
  return (
    &amp;lt;div
      className="draggable"
      draggable
      onDragStart={(e) =&amp;gt; {
        e.dataTransfer.setData('text/plain', 'Hello, World!')
      }}
    &amp;gt;
      Drag me!
    &amp;lt;/div&amp;gt;
  )
}

export default Draggable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this component, we use the &lt;code&gt;draggable&lt;/code&gt; attribute to make the element draggable. This &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable" rel="noopener noreferrer"&gt;attribute&lt;/a&gt; will tell the browser that the element can be dragged.&lt;/p&gt;

&lt;p&gt;We're also reacting to the &lt;code&gt;onDragStart&lt;/code&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragstart_event" rel="noopener noreferrer"&gt;event&lt;/a&gt;. This event is fired by the Drag n Drop API when the user starts dragging the element and can be used to set some properties of the drag operation.&lt;/p&gt;

&lt;p&gt;For example, we use the &lt;code&gt;setData&lt;/code&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/setData" rel="noopener noreferrer"&gt;method&lt;/a&gt; of the &lt;code&gt;dataTransfer&lt;/code&gt; object (that is coming directly from the event itself) to set the data that will be transferred when the element is dragged.&lt;/p&gt;

&lt;p&gt;This works by setting the data type and the data itself. In this case, we set the data type to &lt;code&gt;text/plain&lt;/code&gt; and the data to &lt;code&gt;Hello, World!&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;to set the data that will be transferred when the element is dragged.&lt;/p&gt;

&lt;p&gt;Also, we're setting the class name of the element to &lt;code&gt;draggable&lt;/code&gt; to style it later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Create a Droppable component
&lt;/h3&gt;

&lt;p&gt;Next, let's create a new component called &lt;code&gt;Droppable&lt;/code&gt; that will represent the droppable area.&lt;/p&gt;

&lt;p&gt;We'll create it in the &lt;code&gt;src&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react'

const Droppable: React.FC = () =&amp;gt; {
  return (
    &amp;lt;div
      className="droppable"
      onDragOver={(e) =&amp;gt; {
        e.preventDefault()
      }}
      onDrop={(e) =&amp;gt; {
        e.preventDefault()
        const data = e.dataTransfer.getData('text/plain')
        console.log(data)
      }}
    &amp;gt;
      Drop here!
    &amp;lt;/div&amp;gt;
  )
}

export default Droppable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this component, we're reacting to the &lt;code&gt;onDragOver&lt;/code&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragover_event" rel="noopener noreferrer"&gt;event&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This event is fired by the Drag n Drop API when the user drags an element over a valid drop target and can be used to specify what happens when the element is dragged over the target.&lt;/p&gt;

&lt;p&gt;In this case, we're calling the &lt;code&gt;preventDefault&lt;/code&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault" rel="noopener noreferrer"&gt;method&lt;/a&gt; of the event to prevent the default behavior of the browser, which is &lt;strong&gt;to not allow dropping&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is very important, because by default, the browser does not allow dropping elements on other elements. So we need to call &lt;code&gt;preventDefault&lt;/code&gt; in the onDragOver handler to allow the onDrop handler to be fired.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We're also reacting to the &lt;code&gt;onDrop&lt;/code&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drop_event" rel="noopener noreferrer"&gt;event&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This event is fired by the Drag n Drop API when the user drops an element on a valid drop target and can be used to specify what happens when the element is dropped.&lt;/p&gt;

&lt;p&gt;In this case, we're still calling the &lt;code&gt;preventDefault&lt;/code&gt; and then we're using the &lt;code&gt;getData&lt;/code&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/getData" rel="noopener noreferrer"&gt;method&lt;/a&gt; of the &lt;code&gt;dataTransfer&lt;/code&gt; object to get the data that was set when the element was dragged.&lt;/p&gt;

&lt;p&gt;Also, we're setting the class name of the element to &lt;code&gt;droppable&lt;/code&gt; to style it later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Use the Draggable and Droppable components
&lt;/h3&gt;

&lt;p&gt;Now that we have our &lt;code&gt;Draggable&lt;/code&gt; and &lt;code&gt;Droppable&lt;/code&gt; components, let's use them in the &lt;code&gt;App&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;We'll open the &lt;code&gt;App.tsx&lt;/code&gt; file and replace its content with the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react'
import './App.css'

import Draggable from './Draggable'
import Droppable from './Droppable'

const App: React.FC = () =&amp;gt; {
  return (
    &amp;lt;div className="container"&amp;gt;
      &amp;lt;Draggable /&amp;gt;
      &amp;lt;Droppable /&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export default App
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this component, we're using the &lt;code&gt;Draggable&lt;/code&gt; and &lt;code&gt;Droppable&lt;/code&gt; components that we created earlier. We're also setting the class name of the container to &lt;code&gt;container&lt;/code&gt; to style it later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Style the Draggable and Droppable components
&lt;/h3&gt;

&lt;p&gt;Now that we have our components set up, let's style them.&lt;/p&gt;

&lt;p&gt;We'll open the &lt;code&gt;App.css&lt;/code&gt; file and replace its content with the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.container {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 2rem;
  height: 100vh;
}

.draggable {
  padding: 16px;
  background-color: #f0f0f0;
  cursor: move;
}

.droppable {
  padding: 16px;
  background-color: #f0f0f0;
  cursor: pointer;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this file, we're styling the &lt;code&gt;container&lt;/code&gt;, &lt;code&gt;draggable&lt;/code&gt;, and &lt;code&gt;droppable&lt;/code&gt; elements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Test the Drag and Drop feature
&lt;/h3&gt;

&lt;p&gt;Now that we have our components set up and styled, let's test the Drag and Drop feature.&lt;/p&gt;

&lt;p&gt;We can do this by dragging the &lt;code&gt;Draggable&lt;/code&gt; element and dropping it on the &lt;code&gt;Droppable&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;When we do this, we should see the data that was set when the element was dragged logged to the console.&lt;/p&gt;

&lt;p&gt;If everything is working correctly, you should see &lt;code&gt;Hello, World!&lt;/code&gt; logged to the console, like the following:&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%2Fvkl76jaroae22t6o05qc.gif" 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%2Fvkl76jaroae22t6o05qc.gif" alt="Demo" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Drag and Drop feature is now working correctly! 🎉 The two components are indeed communicating with each other, and the data is being transferred from the draggable element to the droppable element. Starting from this simple example, you can build more complex interactions and behaviors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog post, we learned how to implement a simple Drag and Drop feature in a React application without using any library. We used the HTML5 Drag and Drop API to create a custom Drag and Drop behavior and we created a &lt;code&gt;Draggable&lt;/code&gt; component and a &lt;code&gt;Droppable&lt;/code&gt; component to represent the draggable element and the droppable area.&lt;/p&gt;

&lt;p&gt;I hope you found this blog post helpful and that you learned something new. If you have any questions or feedback, feel free to reach out to me on &lt;a href="https://twitter.com/andreacoluzzi94" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. I'm always happy to help!&lt;/p&gt;

&lt;p&gt;You can see the final code on &lt;a href="https://github.com/coluzziandrea/react-drag-n-drop" rel="noopener noreferrer"&gt;my GitHub profile&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
