<?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: Frank Delporte</title>
    <description>The latest articles on DEV Community by Frank Delporte (@fdelporte).</description>
    <link>https://dev.to/fdelporte</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%2F395015%2Fd00fa8a9-663a-48f3-b950-dd3e5d55278f.jpg</url>
      <title>DEV Community: Frank Delporte</title>
      <link>https://dev.to/fdelporte</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fdelporte"/>
    <language>en</language>
    <item>
      <title>Sheetmusic4J 0.0.3: ABC, Guitar Pro, Engraving Improvements</title>
      <dc:creator>Frank Delporte</dc:creator>
      <pubDate>Thu, 30 Jul 2026 09:20:46 +0000</pubDate>
      <link>https://dev.to/fdelporte/sheetmusic4j-003-abc-guitar-pro-engraving-improvements-4b8a</link>
      <guid>https://dev.to/fdelporte/sheetmusic4j-003-abc-guitar-pro-engraving-improvements-4b8a</guid>
      <description>&lt;p&gt;Last week I &lt;a href="https://dev.to/posts/2026/2026-07-24-introducing-sheetmusic4j/"&gt;introduced Sheetmusic4J&lt;/a&gt;, a Java(FX) library to render and interact with sheet music. That first &lt;strong&gt;0.0.1&lt;/strong&gt; release was really a question more than a product: is there any interest in a native Java(FX) sheet music library before I invest more time in it? So I shared it on social media and waited to see what would come back.&lt;/p&gt;

&lt;p&gt;The answers arrived faster than I expected. And they gave me more homework... &lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/b9dP2HHtKkQ"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Two Comments, Two New Formats
&lt;/h2&gt;

&lt;p&gt;I posted about the release on &lt;a href="https://www.linkedin.com/feed/update/urn:li:activity:7486390283673247744/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, and almost immediately two comments landed with the same kind of challenge: "Nice, but does it support &lt;strong&gt;ABC notation&lt;/strong&gt;?" and "What about &lt;strong&gt;Guitar Pro&lt;/strong&gt; files?"&lt;/p&gt;

&lt;p&gt;That's exactly the feedback loop I hoped the 0.0.1 release would trigger. The whole point of shipping something small and early is to find out where people actually want it to go, instead of guessing. So I took the challenge, wrote plans in Theia IDE with the &lt;a class="mentioned-user" href="https://dev.to/architect"&gt;@architect&lt;/a&gt; and followed up with the @Coder. And now, one week later, &lt;strong&gt;Sheetmusic4J 0.0.3&lt;/strong&gt; is out with support for both formats, plus a lot of polish on how the notation actually looks on the staff. There is also a 0.0.2, but that has a bug which don't correctly load the abc-files...&lt;/p&gt;

&lt;h2&gt;
  
  
  ABC Notation, In and Out
&lt;/h2&gt;

&lt;p&gt;Requested by &lt;a href="https://www.linkedin.com/in/peter-de-coninck-7656b0b4/" rel="noopener noreferrer"&gt;Peter De Coninck&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The headline of this release is &lt;a href="https://sheetmusic4j.com/inspired-by/abc/" rel="noopener noreferrer"&gt;ABC notation&lt;/a&gt; support: the &lt;code&gt;core&lt;/code&gt; module can now both &lt;strong&gt;read and write&lt;/strong&gt; &lt;code&gt;.abc&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;If you've spent time around folk and traditional music, you know ABC. It's a compact, text-based format where an entire tune fits in a few lines of plain text that you can paste into an email, a forum post, or a tune database. It's the exact opposite of MusicXML: terse and human-writable where MusicXML is verbose and tool-oriented. And there are decades of freely shared ABC tunes out there to read.&lt;/p&gt;

&lt;p&gt;Here's what a small tune looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X:1
T:Speed the Plough
M:4/4
L:1/8
K:G
|:GABc dedB|dedB dedB|c2ec B2dB|c2A2 A2BA|
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new &lt;code&gt;AbcReader&lt;/code&gt; and &lt;code&gt;AbcWriter&lt;/code&gt; parse into and generate from the same &lt;code&gt;Score&lt;/code&gt; model as everything else, so once a tune is loaded, the engraving, JavaFX rendering, and MIDI export all just work. Coverage already goes well beyond the basics: keys and modes, unit note length, meter, tuplets, ties and slurs, grace notes, decorations like staccato and rolls, barline styles, repeats and 1st/2nd endings, guitar chord symbols, lyrics, and multi-line titles, all backed by round-trip tests.&lt;/p&gt;

&lt;p&gt;And because &lt;code&gt;ScoreFile&lt;/code&gt; dispatches on the file extension, most applications never touch the reader or writer directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Import: build a Score from an ABC tune&lt;/span&gt;
&lt;span class="nc"&gt;Score&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ScoreFile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;load&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"speed-the-plough.abc"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Export: write a Score back out as ABC&lt;/span&gt;
&lt;span class="nc"&gt;ScoreFile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"speed-the-plough-copy.abc"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ABC is a big enough format that not every corner is covered yet, so real-world tunes that don't render correctly are exactly the kind of bug report I want.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv1idc4izogprni1qq7jb.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv1idc4izogprni1qq7jb.png" alt="Sheet demo application" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Guitar Pro: An Experiment
&lt;/h2&gt;

&lt;p&gt;Requested by &lt;a href="https://www.linkedin.com/in/soheilrahsaz/" rel="noopener noreferrer"&gt;Soheil Rahsaz&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The second new format is more of an experiment. Sheetmusic4J can now do &lt;strong&gt;basic import of &lt;a href="https://sheetmusic4j.com/inspired-by/guitarpro/" rel="noopener noreferrer"&gt;Guitar Pro&lt;/a&gt; 7/8 files&lt;/strong&gt; (&lt;code&gt;.gp&lt;/code&gt;, load only).&lt;/p&gt;

&lt;p&gt;I want to be upfront about what this is. Unlike MusicXML, MIDI, and ABC, Guitar Pro is &lt;strong&gt;not an open format&lt;/strong&gt;. There's no published specification, and this importer is built on the community's documented reverse-engineering of the modern GPIF format, the XML document tucked inside the &lt;code&gt;.gp&lt;/code&gt; ZIP archive. It reads that with the JDK only, no third-party dependency. The older binary formats (&lt;code&gt;.gp3&lt;/code&gt;, &lt;code&gt;.gp4&lt;/code&gt;, &lt;code&gt;.gp5&lt;/code&gt;, &lt;code&gt;.gpx&lt;/code&gt;) are not handled, and there's no saving back.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Import a Guitar Pro 7/8 file (load only)&lt;/span&gt;
&lt;span class="nc"&gt;Score&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ScoreFile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;load&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"song.gp"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added it deliberately early, because I genuinely don't know yet how people would want to use Guitar Pro files here. Is rendering them as standard notation enough, or is tablature-specific rendering the whole point? That's the kind of thing I'd rather learn from real use than guess at. If you have a use case or a file that doesn't import cleanly, &lt;a href="https://github.com/sheetmusic4j/sheetmusic4j/issues" rel="noopener noreferrer"&gt;let me know on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Better-Looking Engraving
&lt;/h2&gt;

&lt;p&gt;The rest of this release is behind-the-scenes work of making the notation look right. Some of the improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grace notes now render as proper small flagged/beamed notes with a connecting curve, and their beam follows the pitch contour of the run. Still not perfect, but a lot better than in the first release.&lt;/li&gt;
&lt;li&gt;Distinct flag glyphs for 32nd/64th/128th notes, and a real double-whole (breve) notehead.&lt;/li&gt;
&lt;li&gt;Ties and slurs sit on the opposite side of the stem by default, and the tuplet bracket disappears when the run already beams cleanly.&lt;/li&gt;
&lt;li&gt;Barline styles, repeat dots and voltas; articulations that hug the notehead; chord symbols closer to the staff; and better spacing around titles and accidentals.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's also a new &lt;code&gt;NoteBackgroundStyle&lt;/code&gt; in the FX viewer to configure the highlight box drawn behind a note (handy for play-along views), and a fix for a crash on very large scores by rendering through a windowed canvas instead of one oversized texture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Under the Hood
&lt;/h2&gt;

&lt;p&gt;The demo app got reorganized and gained a standalone strip play-along demo, the library now logs through SLF4J, the MusicXML reader correctly merges multiple &lt;code&gt;&amp;lt;attributes&amp;gt;&lt;/code&gt; blocks per part instead of overwriting, and MIDI import produces fewer spurious rests.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmtf8rvk9gq2lwkz1yf0j.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmtf8rvk9gq2lwkz1yf0j.png" alt="Strip demo application" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Still 0.0.x, Still Listening
&lt;/h2&gt;

&lt;p&gt;This is version &lt;strong&gt;0.0.3&lt;/strong&gt;, not a stable release. The API and the rendering can still change, and that's the point of publishing this early. The first release asked whether anyone cared; this one is what happened when a couple of people answered in the comments.&lt;/p&gt;

&lt;p&gt;The full list of changes is in the &lt;a href="https://sheetmusic4j.com/releases/" rel="noopener noreferrer"&gt;release notes&lt;/a&gt;, and the &lt;a href="https://sheetmusic4j.com" rel="noopener noreferrer"&gt;code examples&lt;/a&gt; show how to get started. If you try it and hit a rendering difference, or you want a format that isn't there yet, &lt;a href="https://github.com/sheetmusic4j/sheetmusic4j/issues" rel="noopener noreferrer"&gt;issues and feedback on GitHub&lt;/a&gt; are exactly what shapes where this goes next. As it turns out, so is a comment on LinkedIn.&lt;/p&gt;

</description>
      <category>java</category>
      <category>javafx</category>
      <category>sheetmusic</category>
      <category>music</category>
    </item>
    <item>
      <title>Introducing Sheetmusic4J, a Java(FX) Library to Render and Interact with Sheet Music</title>
      <dc:creator>Frank Delporte</dc:creator>
      <pubDate>Fri, 24 Jul 2026 12:01:13 +0000</pubDate>
      <link>https://dev.to/fdelporte/introducing-sheetmusic4j-a-javafx-library-to-render-and-interact-with-sheet-music-48dk</link>
      <guid>https://dev.to/fdelporte/introducing-sheetmusic4j-a-javafx-library-to-render-and-interact-with-sheet-music-48dk</guid>
      <description>&lt;p&gt;I'm building &lt;a href="https://melodymatrix.rocks/" rel="noopener noreferrer"&gt;MelodyMatrix&lt;/a&gt; with my son, an application to look at and play along with music. The app already shows a song in different views: falling blocks, chords, guitar views, and many more. Now we want a Learn section, a view that helps someone practice piano by following the actual sheet music while it plays.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjzpxdckpqa6da3hhdhhx.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjzpxdckpqa6da3hhdhhx.png" alt="Sheetmusic4J in MelodyMatrix" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A static PDF of the sheet doesn't work for that. Great PDF viewer components exist for JavaFX, but a PDF has no connection to what's playing. Nothing highlights the current note, nothing marks the current position in the song. I looked for a JavaFX library that renders sheet music and lets code interact with it: jump to a position, highlight a note, follow playback. I couldn't find one, so I built it.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/D2uaHpvC9ao"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Same Approach as Lottie4J
&lt;/h2&gt;

&lt;p&gt;The problem matched one I'd already solved. With &lt;a href="https://lottie4j.com/" rel="noopener noreferrer"&gt;Lottie4J&lt;/a&gt;, I ran into the same gap: solid animation players exist for the browser and mobile, none for JavaFX. Sheetmusic4J follows the same path: an open file format on one side, a JavaFX rendering component on the other, with AI doing a large part of the implementation work.&lt;/p&gt;

&lt;p&gt;To be clear, this isn't vibe coding. I define the tasks, describe what I want to achieve, review what comes back, and iterate from there. The library wouldn't exist this fast without that collaboration, but the direction and the decisions stay mine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built on MusicXML
&lt;/h2&gt;

&lt;p&gt;Sheetmusic4J reads &lt;a href="https://www.w3.org/2021/06/musicxml40/" rel="noopener noreferrer"&gt;MusicXML&lt;/a&gt;, an open standard for representing sheet music. MIDI stores notes and timing, MusicXML stores a lot more: how a piece is structured, how it should look on the page, lyrics under a melody line for a song with a singer. MusicXML also ships an official set of example files, and I use those to compare what Sheetmusic4J renders against the reference PDF for each file.&lt;/p&gt;

&lt;p&gt;Notation itself follows the &lt;a href="https://www.smufl.org/" rel="noopener noreferrer"&gt;SMuFL&lt;/a&gt; standard for music fonts. Sheetmusic4J currently renders with the Bravura font. I haven't wired up font swapping yet, that's still on the list, but SMuFL makes it possible to support other notation fonts later.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxbirnao0nm28idd1x0nm.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxbirnao0nm28idd1x0nm.png" alt="Notation elements" width="799" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The image above comes straight from the library and maps MusicXML terms to the class names in the Java model and to the layout terms used internally. I'm not a musician, despite years at music school long ago, so I built this reference for myself first. It also gives anyone reporting an issue a shared vocabulary to point at the exact element that's wrong. Check the &lt;a href="https://github.com/sheetmusic4j/sheetmusic4j/blob/main/docs/NOTATION_ELEMENTS.md" rel="noopener noreferrer"&gt;NOTATION_ELEMENTS.md file on GitHub&lt;/a&gt; for more info and a table showing the link between MusicXML elements and how they are used in the library code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://opensheetmusicdisplay.org/" rel="noopener noreferrer"&gt;Open Sheet Music Display&lt;/a&gt; already renders MusicXML in the browser and served as a reference here, the same role the official Lottie web player played for Lottie4J. A WebView could show the same result inside MelodyMatrix, but a WebView doesn't give me a way to talk to the rendering from JavaFX code. Sheetmusic4J does: it exposes the hooks to move a marker, highlight a note, and drive the view from a MIDI stream, which is exactly what MelodyMatrix needs to sync a piano performance with the sheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four Repositories, One Library
&lt;/h2&gt;

&lt;p&gt;Everything lives in the &lt;a href="https://github.com/sheetmusic4j" rel="noopener noreferrer"&gt;sheetmusic4j&lt;/a&gt; organization on GitHub, with the &lt;a href="https://github.com/sheetmusic4j/sheetmusic4j" rel="noopener noreferrer"&gt;library project&lt;/a&gt; structured close to Lottie4J:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core&lt;/strong&gt;: reads and writes MusicXML and MIDI files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Engraver&lt;/strong&gt;: turns that raw data into the layout of a sheet, with no JavaFX dependency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FX Viewer&lt;/strong&gt;: the JavaFX component applications use to show a sheet, including a full-page sheet view and a horizontal strip view.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo&lt;/strong&gt;: a test application for the other three modules.&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9w84m3uxzi5lx0wwzff3.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9w84m3uxzi5lx0wwzff3.png" alt="Demo application" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The demo app renders the same MusicXML file two ways side by side: a static PDF (using &lt;a href="https://github.com/dlsc-software-consulting-gmbh/PDFViewFX" rel="noopener noreferrer"&gt;Derek Lemmerman's PDF viewer component&lt;/a&gt;) next to the Sheetmusic4J FX Viewer. The PDF stays fixed to its page size. The FX Viewer reflows the layout as the window resizes, which occasionally shifts a line differently than the PDF does. The demo also includes tools to simulate playback and highlight notes, without any sound, purely to test the visual sync. A diff tab compares the FX Viewer output pixel by pixel against the reference PDF. It surfaces real differences, though I'm still figuring out how useful that comparison is given that a static PDF and an interactive viewer solve different problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Library
&lt;/h2&gt;

&lt;p&gt;Add the FX Viewer dependency to a JavaFX project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.sheetmusic4j&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;fxviewer&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.0.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Load a MusicXML file into a sheet view with a few lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Score&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ScoreFile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;load&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path/to/song.musicxml"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;SheetView&lt;/span&gt; &lt;span class="n"&gt;sheetView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SheetView&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;sheetView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setScore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setScene&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Scene&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ScrollPane&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sheetView&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The FX Viewer module also ships a &lt;code&gt;StripView&lt;/code&gt; for a horizontal, scrolling layout of the same score.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version 0.0.1, and What's Next
&lt;/h2&gt;

&lt;p&gt;This first release carries version 0.0.1, not a stable release. A lot of the API and the rendering can still change. From here, MelodyMatrix's Learn section drives most of the requirements: following a marker across the sheet, highlighting the note currently played, keeping the view in sync with MIDI input from a real piano.&lt;/p&gt;

&lt;p&gt;If you try it and hit a rendering difference, open an issue in the &lt;a href="https://github.com/sheetmusic4j/sheetmusic4j" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; with the MusicXML file and a screenshot of what you expected versus what you got. That's exactly the kind of report that moved Lottie4J forward fast, and I expect it works the same way here. Check the code, the docs, and the demo app on &lt;a href="https://sheetmusic4j.com" rel="noopener noreferrer"&gt;sheetmusic4j.com&lt;/a&gt;. Use the dependency from Maven or fork the repository, and let me know what you build with it.&lt;/p&gt;

</description>
      <category>java</category>
      <category>javafx</category>
      <category>sheetmusic</category>
      <category>melodymatrix</category>
    </item>
    <item>
      <title>First Test of Java on Banana Pi (ARM and RISC-V), Plus a Blinking LED with Pi4J</title>
      <dc:creator>Frank Delporte</dc:creator>
      <pubDate>Thu, 16 Jul 2026 14:58:44 +0000</pubDate>
      <link>https://dev.to/fdelporte/first-test-of-java-on-banana-pi-arm-and-risc-v-plus-a-blinking-led-with-pi4j-19o0</link>
      <guid>https://dev.to/fdelporte/first-test-of-java-on-banana-pi-arm-and-risc-v-plus-a-blinking-led-with-pi4j-19o0</guid>
      <description>&lt;p&gt;As part of my 2026 learning goals around Java on RISC-V (see &lt;a href="https://webtechie.be/post/2026-01-07-x86-arm-riscv/" rel="noopener noreferrer"&gt;this post about x86 versus ARM versus RISC-V&lt;/a&gt;), I've asked various suppliers to send me evaluation boards. I already published these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webtechie.be/post/first-experiments-with-java-on-the-lattepanda-iota-an-alternative-to-raspberry-pi/" rel="noopener noreferrer"&gt;LattePanda IOTA (x86)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webtechie.be/post/first-test-of-java-on-the-orange-pi-arm-and-risc-v/" rel="noopener noreferrer"&gt;OrangePi 5 Ultra (ARM) and OrangePi RV2 (RISC-V)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webtechie.be/post/first-test-of-java-on-the-starfive-visionfive-2-lite-risc-v/" rel="noopener noreferrer"&gt;VisionFive 2 Lite (RISC-V)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webtechie.be/post/first-test-of-java-on-beagleboards-arm-and-risc-v/" rel="noopener noreferrer"&gt;BeagleBoards (ARM and RISC-V)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webtechie.be/post/i-got-java-25-running-on-the-risc-v-beagleboard-beaglev-fire/" rel="noopener noreferrer"&gt;Java 25 on BeagleV-First&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;In this post: Banana Pi's&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I got all these boards for free, but what I write here and show in the video is not controlled by the suppliers.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/78Jm2sR33fI"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  ARM versus RISC-V?
&lt;/h2&gt;

&lt;p&gt;ARM and RISC-V represent two different approaches to processor design. ARM is the established player we know from, e.g., the Raspberry Pi's. It's mature, and has a huge ecosystem of tools and support built over decades. RISC-V is the open-source alternative, free from licensing restrictions and fully transparent. While ARM still leads in performance and tooling today, RISC-V is catching up fast. The real difference isn't just about speed. It's about openness and flexibility. With RISC-V, you're not locked into a vendor's ecosystem, and you have complete visibility into how your hardware works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Banana Pi
&lt;/h2&gt;

&lt;p&gt;Here's how the Banana Pi's I received, compare to some of the Raspberry Pi's. If you compare the Raspberry Pi prices with my earlier articles from +-6 months ago, you'll see that the prices have gone up a lot. The shortage of memory chips is really a problem and making these single-board-computers less attractive...&lt;/p&gt;

&lt;p&gt;The Banana Pi BPI-M4 Zero was 44€ earlier this year, but now at 148€! The BPI-F3 went from 84€ to 214€...&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Board&lt;/th&gt;
&lt;th&gt;SOC&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;CPU&lt;/th&gt;
&lt;th&gt;Cores&lt;/th&gt;
&lt;th&gt;Speed&lt;/th&gt;
&lt;th&gt;Early 2026&lt;/th&gt;
&lt;th&gt;July 2026&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://api.pi4j.com/board-information/MODEL_4_B" rel="noopener noreferrer"&gt;Raspberry Pi 4&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;BCM2711&lt;/td&gt;
&lt;td&gt;ARMv8&lt;/td&gt;
&lt;td&gt;Cortex-A72&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1.8Ghz&lt;/td&gt;
&lt;td&gt;68€&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com.be/-/en/Raspberry-Pi-Model-4GB-LPDDR4/dp/B09TTNF8BT" rel="noopener noreferrer"&gt;128€ (4GB)&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://api.pi4j.com/board-information/MODEL_5_B" rel="noopener noreferrer"&gt;Raspberry Pi 5&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;BCM2712&lt;/td&gt;
&lt;td&gt;ARMv8&lt;/td&gt;
&lt;td&gt;Cortex-A76&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2.4Ghz&lt;/td&gt;
&lt;td&gt;79€&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com.be/-/en/Raspberry-4GB-Quad-Core-ARMA76-64-bit/dp/B0CK3L9WD3" rel="noopener noreferrer"&gt;144€ (4GB)&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://api.pi4j.com/board-information/ZERO_V2" rel="noopener noreferrer"&gt;Raspberry Pi Zero 2&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;BCM2710A1&lt;/td&gt;
&lt;td&gt;ARMv8&lt;/td&gt;
&lt;td&gt;Cortex-A53&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1Ghz&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.kiwi-electronics.com/nl/raspberry-pi-zero-2-w-met-header-20130?search=zero%202" rel="noopener noreferrer"&gt;21€ (0.5GB)&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.banana-pi.org/en/banana-pi-sbcs/171.html" rel="noopener noreferrer"&gt;Banana Pi BPI-M4 Zero&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;H618&lt;/td&gt;
&lt;td&gt;ARMv8&lt;/td&gt;
&lt;td&gt;Cortex-A53&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;1.5Ghz&lt;/td&gt;
&lt;td&gt;44€&lt;/td&gt;
&lt;td&gt;&lt;a href="https://openelab.io/products/banana-pi-bpi-m4-zero" rel="noopener noreferrer"&gt;148€ (4GB+32GB)&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.banana-pi.org/en/banana-pi-sbcs/175.html" rel="noopener noreferrer"&gt;Banana Pi BPI-F3&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;SpacemiT K1&lt;/td&gt;
&lt;td&gt;RISC-V&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;84€&lt;/td&gt;
&lt;td&gt;&lt;a href="https://banana-pi.eu/produit/banana-pi-bpi-f3/" rel="noopener noreferrer"&gt;214€ (16GB)&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  First Impressions
&lt;/h3&gt;

&lt;p&gt;Banana Pi suffers from the same problem as a lot of single-board-computer suppliers. They make great hardware but forget the user experience to get started... There is no tool available to select the board and create an SD card like you can do with the Raspberry Pi Imager tool. So you have to search through the documentation, ignore some broken links, and search further to find the right image...&lt;/p&gt;

&lt;p&gt;Below are the links I found with OS images for the Banana Pi boards and burned them to an SD card with the Raspberry Pi Imager tool. In the "Device" step select "No filtering", and in the "OS" step select "Other" and browse to the image file you have downloaded (and unzipped).&lt;/p&gt;

&lt;h3&gt;
  
  
  Banana Pi BPI-M4 Zero
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Banana Pi BPI-M4 Zero is the successor model of BPI-M2 Zero. The SOC is upgraded to Allwinner  H618 quad-core A53 . The memory is upgraded to 2G  LPDDR4, and 8G eMMC onboard . It supports 5G WiFi and BT, and the USB interface has also been upgraded to type-C. It has the same form factor and 40-pin connector as the Raspberry Pi Zero W, and it will fit most Zero W cases and accessories.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.banana-pi.org/en/banana-pi-sbcs/171.html" rel="noopener noreferrer"&gt;Product page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.banana-pi.org/en/BPI-M4_Zero/BananaPi_BPI-M4_Zero" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.banana-pi.org/en/BPI-M4_Zero/GettingStarted_BPI-M4_Zero" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://drive.google.com/drive/folders/1-MzSVsduPX8qHKgbAOM3wmcCbwfkffAz" rel="noopener noreferrer"&gt;Operating System&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;I used "20241011_Bananapi-Armbian_24.8.2_Bpi-m4zero_ubuntu22.04_jammy_legacy_5.4.125_xfce_desktop.zip"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn6wnhqqq0a5cypxa1ryv.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn6wnhqqq0a5cypxa1ryv.png" alt="Banana Pi BPI-M4 Zero with connected RGB LED" width="800" height="619"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Banana Pi BPI-F3
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Banana Pi BPI-F3 is a industrial grade RISC-V development board, it design with SpacemiT K1 8 core RISC-V chip, CPU integrates 2.0 TOPs AI computing power. 2/4/8/16G DDR and 8/16/32/128G eMMC onboard.2x GbE Ethernet prot, 4x USB 3.0 and PCIe for M.2 interface, supprt HDMI and Dual MIPI-CSI Camera.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.banana-pi.org/en/banana-pi-sbcs/175.html" rel="noopener noreferrer"&gt;Product page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.banana-pi.org/en/BPI-F3/BananaPi_BPI-F3" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.banana-pi.org/en/BPI-F3/GettingStarted_BPI-F3" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://archive.spacemit.com/image/k1/version/bianbu/" rel="noopener noreferrer"&gt;Operating System&lt;/a&gt; 

&lt;ul&gt;
&lt;li&gt;Select the image ending in "bianbu-k1-xxx.img.zip"&lt;/li&gt;
&lt;li&gt;I used "bianbu-25.04-desktop-k1-v3.0.1-release-20250815185656.img.zip"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffc3bzpnc2aitmgid9e8d.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffc3bzpnc2aitmgid9e8d.jpg" alt="Banana Pi vs Raspberry Pi" width="800" height="758"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing with Java and Pi4J
&lt;/h2&gt;

&lt;p&gt;I started with a lot of reserves... Both OS images are pretty outdated (2024 and 2025), so I'm wondering what I will find when I boot the boards. I also have no idea if the Banana Pi BPI-F3 RISC-V board will run Java 25, as it is a very new board and the OS image is already a bit outdated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Banana Pi BPI-M4 Zero
&lt;/h3&gt;

&lt;p&gt;The Zero boots in terminal mode, and after the first boot asks to create a root password, user account, and WiFi credentials. After that, it's ready to be used and accessible via SSH. I used a USB-C dongle to connect keyboard, mouse, and network to get started quickly. After doing the usual steps I used on the previous boards I tested, I can confirm that Java runs fine on the Banana Pi BPI-M4 Zero. I installed Java 25 (25.0.3-zulu) and JBang, and ran the &lt;code&gt;HelloWorld.java&lt;/code&gt; and &lt;code&gt;JsonParsing.java&lt;/code&gt; examples from the &lt;a href="https://github.com/Pi4J/pi4j-jbang" rel="noopener noreferrer"&gt;Pi4J JBang repository&lt;/a&gt;. The result is shown in the video.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;zip
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"https://get.sdkman.io"&lt;/span&gt; | bash

&lt;span class="c"&gt;# Close the terminal and open a new one&lt;/span&gt;
sdk &lt;span class="nb"&gt;install &lt;/span&gt;java 25.0.3-zulu-fx
sdk &lt;span class="nb"&gt;install &lt;/span&gt;jbang

&lt;span class="c"&gt;# Close the terminal and open a new one&lt;/span&gt;
git clone https://github.com/Pi4J/pi4j-jbang.git
&lt;span class="nb"&gt;cd &lt;/span&gt;pi4j-jbang
&lt;span class="nb"&gt;cd &lt;/span&gt;basic
java HelloWorld.java
jbang JsonParsing.java  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also executed my &lt;a href="https://webtechie.be/sbc/" rel="noopener noreferrer"&gt;SBC Java benchmark test&lt;/a&gt; so we can compare the performance of this board with other single-board-computers. Results below...&lt;/p&gt;

&lt;h3&gt;
  
  
  Pi4j on the Banana Pi Zero
&lt;/h3&gt;

&lt;p&gt;In the &lt;a href="https://docs.banana-pi.org/en/BPI-M4_Zero/BananaPi_BPI-M4_Zero" rel="noopener noreferrer"&gt;documentation of th Banana Pi BPI-M4 Zero&lt;/a&gt; I found a GPIO table that looks similar to the 40-pin layout of the Raspberry Pi. And as we are running the same kind of Linux used in the Raspberry Pi OS, we should be able to use Pi4J V4, based on the Foreign Function and Memory API (FFM API) to access the GPIO pins. I tried to run &lt;code&gt;jbang RgbLed.java&lt;/code&gt; from the Pi4J JBang repo.&lt;/p&gt;

&lt;p&gt;Sidenote: this board comes without a header, so I soldered a 40-pin header but struggled a lot! For some odd reason, the solder doesn't stick to the board. So it took me a lot of time and removing solder that got stuck to two pins and then trying again. I finally got it done, but it was a lot of work and hope the soldering is good enough to make a reliable connection.&lt;/p&gt;

&lt;p&gt;After some struggling with the user rights, using a script from the &lt;a href="https://github.com/Pi4J/pi4j-os/blob/main/script/setup-permissions.sh" rel="noopener noreferrer"&gt;Pi4J OS repository&lt;/a&gt;, I got passed exceptions thrown by the FFM plugin in Pi4J, but still got blocked with the following output. Probably a mismatch between GPIO addresses, or an outdated Linux kernel, something to figure out later... It's also strange that Pi4J recognizes this board as a Raspberry Pi 1 Model B, while it is a Banana Pi BPI-M4 Zero. Seems this Banana Pi has the same board code as the Raspberry Pi 1 Model B...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;jbang RgbLed.java 
&lt;span class="o"&gt;[&lt;/span&gt;main] INFO com.pi4j.Pi4J - Pi4J library build info:
&lt;span class="o"&gt;[&lt;/span&gt;main] INFO com.pi4j.Pi4J -     Version: 4.0.2
&lt;span class="o"&gt;[&lt;/span&gt;main] INFO com.pi4j.Pi4J -     Timestamp: 2026-06-08T10:01:34Z
...
&lt;span class="o"&gt;[&lt;/span&gt;main] INFO com.pi4j.boardinfo.util.BoardInfoHelper - Detected OS: Name: Linux, version: 5.4.125-legacy-sunxi64, architecture: aarch64
&lt;span class="o"&gt;[&lt;/span&gt;main] INFO com.pi4j.boardinfo.util.BoardInfoHelper - Detected Java: Version: 25.0.3, runtime: 25.0.3+9-LTS, vendor: Azul Systems, Inc., vendor version: Zulu25.34+17-CA
&lt;span class="o"&gt;[&lt;/span&gt;main] INFO com.pi4j.boardinfo.util.BoardInfoHelper - Detected board &lt;span class="nb"&gt;type &lt;/span&gt;MODEL_1_B by code: 0003
&lt;span class="o"&gt;[&lt;/span&gt;main] INFO com.pi4j.context.impl.DefaultContext - Detected board model: Raspberry Pi 1 Model B
&lt;span class="o"&gt;[&lt;/span&gt;main] INFO com.pi4j.context.impl.DefaultContext - Running on: Name: Linux, version: 5.4.125-legacy-sunxi64, architecture: aarch64
&lt;span class="o"&gt;[&lt;/span&gt;main] INFO com.pi4j.context.impl.DefaultContext - With Java version: Version: 25.0.3, runtime: 25.0.3+9-LTS, vendor: Azul Systems, Inc., vendor version: Zulu25.34+17-CA
...
&lt;span class="o"&gt;[&lt;/span&gt;main] INFO com.pi4j.plugin.ffm.providers.gpio.FFMDigitalOutput - /dev/gpiochip0-23 - setting up DigitalOutput BCM...
Exception &lt;span class="k"&gt;in &lt;/span&gt;thread &lt;span class="s2"&gt;"main"&lt;/span&gt; com.pi4j.exception.Pi4JException: Error during call to method &lt;span class="s1"&gt;'call'&lt;/span&gt; with data &lt;span class="s1"&gt;'[9, 3238048773, LineInfo{name=()[], consumer=()[], offset=23, numAttrs=0, flags=0, attrs=[]}]'&lt;/span&gt;: Invalid argument &lt;span class="o"&gt;(&lt;/span&gt;22&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.plugin.ffm.common.ioctl.IoctlNative.call&lt;span class="o"&gt;(&lt;/span&gt;IoctlNative.java:106&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.plugin.ffm.providers.gpio.FFMDigitalOutput.initialize&lt;span class="o"&gt;(&lt;/span&gt;FFMDigitalOutput.java:55&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.plugin.ffm.providers.gpio.FFMDigitalOutput.initialize&lt;span class="o"&gt;(&lt;/span&gt;FFMDigitalOutput.java:24&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.registry.impl.DefaultRuntimeRegistry.add&lt;span class="o"&gt;(&lt;/span&gt;DefaultRuntimeRegistry.java:95&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.registry.impl.DefaultRegistry.add&lt;span class="o"&gt;(&lt;/span&gt;DefaultRegistry.java:103&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.plugin.ffm.providers.gpio.FFMDigitalOutputProviderImpl.create&lt;span class="o"&gt;(&lt;/span&gt;FFMDigitalOutputProviderImpl.java:25&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.plugin.ffm.providers.gpio.FFMDigitalOutputProviderImpl.create&lt;span class="o"&gt;(&lt;/span&gt;FFMDigitalOutputProviderImpl.java:12&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.io.gpio.digital.DigitalOutputProvider.create&lt;span class="o"&gt;(&lt;/span&gt;DigitalOutputProvider.java:58&lt;span class="o"&gt;)&lt;/span&gt;
    at RgbLed.main&lt;span class="o"&gt;(&lt;/span&gt;RgbLed.java:38&lt;span class="o"&gt;)&lt;/span&gt;
Caused by: com.pi4j.exception.Pi4JException: Error during call to method &lt;span class="s1"&gt;'call'&lt;/span&gt; with data &lt;span class="s1"&gt;'[9, 3238048773, LineInfo{name=()[], consumer=()[], offset=23, numAttrs=0, flags=0, attrs=[]}]'&lt;/span&gt;: Invalid argument &lt;span class="o"&gt;(&lt;/span&gt;22&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.plugin.ffm.common.Pi4JNativeContext.processError&lt;span class="o"&gt;(&lt;/span&gt;Pi4JNativeContext.java:58&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.plugin.ffm.common.ioctl.IoctlNative.call&lt;span class="o"&gt;(&lt;/span&gt;IoctlNative.java:103&lt;span class="o"&gt;)&lt;/span&gt;
    ... 8 more
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After some researching, I &lt;a href="https://armbian.com/boards/bananapim4zero" rel="noopener noreferrer"&gt;found a newer OS for this board&lt;/a&gt; and downloaded a version with Xfce desktop from &lt;a href="https://dl.armbian.com/bananapim4zero/Noble_current_xfce" rel="noopener noreferrer"&gt;here&lt;/a&gt;. With this newer version of the OS, and the correctly configured user rights, we get another error. That's progress! ;-)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;jbang RgbLed.java 
...
&lt;span class="o"&gt;[&lt;/span&gt;main] INFO com.pi4j.boardinfo.util.BoardInfoHelper - Detected OS: Name: Linux, version: 6.18.33-current-sunxi64, architecture: aarch64
...
&lt;span class="o"&gt;[&lt;/span&gt;main] INFO com.pi4j.context.impl.DefaultContext - With Java version: Version: 25.0.3, runtime: 25.0.3+9-LTS, vendor: Azul Systems, Inc., vendor version: Zulu25.34+17-CA
...
&lt;span class="o"&gt;[&lt;/span&gt;main] INFO com.pi4j.runtime.impl.DefaultRuntime - Pi4J context/runtime successfully initialized.
&lt;span class="o"&gt;[&lt;/span&gt;main] INFO com.pi4j.plugin.ffm.providers.gpio.FFMDigitalOutput - /dev/gpiochip0-23 - setting up DigitalOutput BCM...
Exception &lt;span class="k"&gt;in &lt;/span&gt;thread &lt;span class="s2"&gt;"main"&lt;/span&gt; com.pi4j.exception.Pi4JException: Error during call to method &lt;span class="s1"&gt;'call'&lt;/span&gt; with data &lt;span class="s1"&gt;'[9, 3260068871, LineRequest{offsets=[23], consumer=(pi4j.FFMDigitalOutput)[112, 105, 52, 106, 46, 70, 70, 77, 68, 105, 103, 105, 116, 97, 108, 79, 117, 116, 112, 117, 116], config=LineConfig{flags=8, numAttrs=0, attrs=[]}, numLines=1, eventBufferSize=0, fd=0}]'&lt;/span&gt;: Unknown error 517 &lt;span class="o"&gt;(&lt;/span&gt;517&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.plugin.ffm.common.ioctl.IoctlNative.call&lt;span class="o"&gt;(&lt;/span&gt;IoctlNative.java:106&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.plugin.ffm.providers.gpio.FFMDigitalOutput.initialize&lt;span class="o"&gt;(&lt;/span&gt;FFMDigitalOutput.java:64&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.plugin.ffm.providers.gpio.FFMDigitalOutput.initialize&lt;span class="o"&gt;(&lt;/span&gt;FFMDigitalOutput.java:24&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.registry.impl.DefaultRuntimeRegistry.add&lt;span class="o"&gt;(&lt;/span&gt;DefaultRuntimeRegistry.java:95&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.registry.impl.DefaultRegistry.add&lt;span class="o"&gt;(&lt;/span&gt;DefaultRegistry.java:103&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.plugin.ffm.providers.gpio.FFMDigitalOutputProviderImpl.create&lt;span class="o"&gt;(&lt;/span&gt;FFMDigitalOutputProviderImpl.java:25&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.plugin.ffm.providers.gpio.FFMDigitalOutputProviderImpl.create&lt;span class="o"&gt;(&lt;/span&gt;FFMDigitalOutputProviderImpl.java:12&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.io.gpio.digital.DigitalOutputProvider.create&lt;span class="o"&gt;(&lt;/span&gt;DigitalOutputProvider.java:58&lt;span class="o"&gt;)&lt;/span&gt;
    at RgbLed.main&lt;span class="o"&gt;(&lt;/span&gt;RgbLed.java:38&lt;span class="o"&gt;)&lt;/span&gt;
Caused by: com.pi4j.exception.Pi4JException: Error during call to method &lt;span class="s1"&gt;'call'&lt;/span&gt; with data &lt;span class="s1"&gt;'[9, 3260068871, LineRequest{offsets=[23], consumer=(pi4j.FFMDigitalOutput)[112, 105, 52, 106, 46, 70, 70, 77, 68, 105, 103, 105, 116, 97, 108, 79, 117, 116, 112, 117, 116], config=LineConfig{flags=8, numAttrs=0, attrs=[]}, numLines=1, eventBufferSize=0, fd=0}]'&lt;/span&gt;: Unknown error 517 &lt;span class="o"&gt;(&lt;/span&gt;517&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.plugin.ffm.common.Pi4JNativeContext.processError&lt;span class="o"&gt;(&lt;/span&gt;Pi4JNativeContext.java:58&lt;span class="o"&gt;)&lt;/span&gt;
    at com.pi4j.plugin.ffm.common.ioctl.IoctlNative.call&lt;span class="o"&gt;(&lt;/span&gt;IoctlNative.java:103&lt;span class="o"&gt;)&lt;/span&gt;
    ... 8 more
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a Raspberry Pi, the numbers you pass to Pi4J directly match the SoC's own "BCM" GPIO numbering, so pin 16 on the header is simply BCM 23, no translation needed. The &lt;a href="https://docs.banana-pi.org/en/BPI-M4_Zero/BananaPi_BPI-M4_Zero" rel="noopener noreferrer"&gt;Banana Pi M4 Zero&lt;/a&gt; uses a different SoC (Allwinner H618) with its own GPIO controller and numbering scheme, so that shortcut doesn't apply, even though the board has the same 40-pin header layout as a Raspberry Pi Zero.&lt;/p&gt;

&lt;p&gt;The first clue was Banana Pi's own &lt;a href="https://docs.banana-pi.org/en/BPI-M4_Zero/BananaPi_BPI-M4_Zero#_gpio_pin_define" rel="noopener noreferrer"&gt;pinout table&lt;/a&gt;, which lists physical pin 16 as &lt;code&gt;PI15&lt;/code&gt; = GPIO bank &lt;code&gt;I&lt;/code&gt;, pin &lt;code&gt;15&lt;/code&gt;. Allwinner chips group their GPIOs into lettered banks of 32 pins each (&lt;code&gt;PA0&lt;/code&gt;-&lt;code&gt;PA31&lt;/code&gt;, &lt;code&gt;PB0&lt;/code&gt;-&lt;code&gt;PB31&lt;/code&gt;, and so on — see &lt;a href="https://linux-sunxi.org/GPIO" rel="noopener noreferrer"&gt;linux-sunxi.org/GPIO&lt;/a&gt; for the general convention), and the Linux kernel exposes this hardware as character devices under &lt;code&gt;/dev/gpiochip*&lt;/code&gt;. Running &lt;code&gt;gpioinfo&lt;/code&gt; on the board showed two chips: &lt;code&gt;gpiochip0&lt;/code&gt; with 32 lines, and &lt;code&gt;gpiochip1&lt;/code&gt; with 288 lines. 288 is exactly 9 banks of 32, which matches banks A through I. This indicates that &lt;code&gt;gpiochip1&lt;/code&gt; is the controller behind the 40-pin header, not &lt;code&gt;gpiochip0&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; gpiod
&lt;span class="nv"&gt;$ &lt;/span&gt;gpioinfo
gpiochip0 - 32 lines:
    line   0:      unnamed       kernel   input  active-high &lt;span class="o"&gt;[&lt;/span&gt;used]
    line   1:      unnamed       kernel   input  active-high &lt;span class="o"&gt;[&lt;/span&gt;used]
    line   2:      unnamed       unused   input  active-high 
    ...
gpiochip1 - 288 lines:
    line   0:      unnamed       unused   input  active-high 
    line   1:      unnamed       unused   input  active-high 
    line   2:      unnamed       unused   input  active-high 
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To find the pin number to use in the code, we need to do some calculation: the index of the bank (A=0, B=1, C=2, ... I=8) multiplied by 32, plus the pin number within that bank, to calculate the absolute line offset on the chip. For &lt;code&gt;PI15&lt;/code&gt;, that's &lt;code&gt;8 × 32 + 15 = 271&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can verify this with &lt;a href="https://packages.debian.org/bookworm/gpiod" rel="noopener noreferrer"&gt;&lt;code&gt;libgpiod&lt;/code&gt;&lt;/a&gt;, a command-line tools to toggle GPIO lines. As we already installed &lt;code&gt;gpiod&lt;/code&gt;, we can use &lt;code&gt;gpioset&lt;/code&gt; to set a line high or low. For example, to toggle the red LED connected to physical pin 16, we can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gpioset gpiochip1 &lt;span class="nv"&gt;271&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1   &lt;span class="c"&gt;# Red on&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gpioset gpiochip1 &lt;span class="nv"&gt;271&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0   &lt;span class="c"&gt;# Red off&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The red LED turned on and off, confirming line 271 on &lt;code&gt;gpiochip1&lt;/code&gt; really is physical pin 16. I repeated the same process for green (&lt;code&gt;PI16&lt;/code&gt; → line 272) and blue — except blue's original pin (physical pin 22) turned out to be a 3.3V power pin on this board, not a GPIO at all. So I moved that wire to a real GPIO pin (physical pin 23, &lt;code&gt;PH6&lt;/code&gt;, bank H → &lt;code&gt;7 × 32 + 6 = 230&lt;/code&gt;) before it could work.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Color&lt;/th&gt;
&lt;th&gt;Physical pin&lt;/th&gt;
&lt;th&gt;Offset (gpiochip1)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Red&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;271&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Green&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;272&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blue&lt;/td&gt;
&lt;td&gt;23 (moved from 22)&lt;/td&gt;
&lt;td&gt;230&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;After testing these values again with &lt;code&gt;gpioset&lt;/code&gt;, I could modify the JBang &lt;code&gt;RgbLed.java&lt;/code&gt; script. We need to add &lt;code&gt;.bus(1)&lt;/code&gt; to target &lt;code&gt;gpiochip1&lt;/code&gt;, and change the BCM values with &lt;code&gt;.bcm(271)&lt;/code&gt;. These are the lines to be added/changed in the &lt;a href="https://github.com/Pi4J/pi4j-jbang/blob/main/digital/RgbLed.java" rel="noopener noreferrer"&gt;original code&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.pi4j.io.gpio.digital.DigitalOutput&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.pi4j.io.gpio.digital.DigitalState&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// Connect a LED to PIN 16 = gpiochip1 line 271&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="no"&gt;GPIO_RED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;271&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Connect a LED to PIN 18 = gpiochip1 line 272&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="no"&gt;GPIO_GREEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;272&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Connect a LED to PIN 23 = gpiochip1 line 230 (moved from PIN 22, which is 3.3V, not a GPIO pin on this board)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="no"&gt;GPIO_BLUE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;230&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Initialize the Pi4J context&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pi4j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pi4J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newAutoContext&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Initialize the three LEDs on gpiochip1 (this board's main GPIO controller)&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ledRed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pi4j&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DigitalOutput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newConfigBuilder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pi4j&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"red"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Red LED"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;bus&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;bcm&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;GPIO_RED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shutdown&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DigitalState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LOW&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;initial&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DigitalState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LOW&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ledGreen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pi4j&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DigitalOutput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newConfigBuilder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pi4j&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"green"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Green LED"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;bus&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;bcm&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;GPIO_GREEN&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shutdown&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DigitalState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LOW&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;initial&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DigitalState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LOW&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ledBlue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pi4j&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DigitalOutput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newConfigBuilder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pi4j&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"blue"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Blue LED"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;bus&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;bcm&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;GPIO_BLUE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shutdown&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DigitalState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LOW&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;initial&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DigitalState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LOW&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;// Keep the remaining code the same&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now executing &lt;code&gt;jbang RgbLed.java&lt;/code&gt; finally worked, and the RGB LED started blinking in the expected colors. Success! This is my very first working Pi4J example on a non-Raspberry Pi board!!! One of my 2026-goals is achieved :-)&lt;/p&gt;

&lt;h3&gt;
  
  
  Banana Pi BPI-F3
&lt;/h3&gt;

&lt;p&gt;The Banana Pi BPI-F3 boots in desktop mode! So that's a lot nicer for a first test ;-) As this board has a RISC-V processor, we can't use SDKMAN to install Java as we learned from experiments with other RISC-V boards before. So I installed &lt;code&gt;openjdk-25-jdk&lt;/code&gt; with &lt;code&gt;apt&lt;/code&gt;, but to my surprise, this installed an &lt;code&gt;ea&lt;/code&gt; (Early Access) version. Maybe because the OS is outdated and got stuck on older JDK releases? Anyhow, I could run the &lt;code&gt;HelloWorld.java&lt;/code&gt; and &lt;code&gt;JsonParsing.java&lt;/code&gt; examples again without a problem, and ran the SBC benchmark.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmark Results
&lt;/h2&gt;

&lt;p&gt;To have a fair comparison in the benchmarks, I also ran one on a Raspberry Pi Zero 2 as this is the closest competitor to the Banana Pi BPI-M4 Zero. The full results are available on the &lt;a href="https://webtechie.be/sbc/" rel="noopener noreferrer"&gt;SBC Java benchmark page&lt;/a&gt;, where you can compare them with the other boards I tested before. &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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvugy4lakyneostofw47l.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvugy4lakyneostofw47l.png" alt="Overview of all the benchmarks" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Banana Pi BPI-M4 Zero beats its direct rival, the Raspberry Pi Zero 2, on every single benchmark. &lt;code&gt;fj-kmeans&lt;/code&gt; finished in 64.8s on the BPI-M4 Zero, while the Raspberry Pi Zero 2 didn't even finish within the 10-minute timeout. On the four benchmarks both boards did complete, the BPI-M4 Zero was 35-47% faster, e.g. &lt;code&gt;mnemonics&lt;/code&gt; (31.1s vs 58.3s) and &lt;code&gt;future-genetic&lt;/code&gt; (23.6s vs 41.6s).&lt;/li&gt;
&lt;li&gt;The tiny, cheap BPI-M4 Zero even outran the pricier BeagleY-AI on all five shared benchmarks, by 5% (&lt;code&gt;fj-kmeans&lt;/code&gt;) up to 24% (&lt;code&gt;future-genetic&lt;/code&gt;). Not bad for the smallest and cheapest board in the whole comparison!&lt;/li&gt;
&lt;li&gt;On the RISC-V side, the BPI-F3 is roughly in the same league as the OrangePi RV2 (within 2-5% of each other on every benchmark), and clearly ahead of the older BeagleV-Fire, which timed out on &lt;code&gt;fj-kmeans&lt;/code&gt;, &lt;code&gt;mnemonics&lt;/code&gt;, and &lt;code&gt;par-mnemonics&lt;/code&gt; altogether. On the benchmarks BeagleV-Fire did finish, the BPI-F3 was 2.6-2.8x faster.&lt;/li&gt;
&lt;li&gt;Compared to the StarFive VisionFive 2 Lite, the BPI-F3 is almost twice as fast on the heavily multi-threaded &lt;code&gt;fj-kmeans&lt;/code&gt; (33.0s vs 62.3s), but the VisionFive 2 edges ahead by 10-17% on the more single-threaded &lt;code&gt;scala-kmeans&lt;/code&gt; and &lt;code&gt;future-genetic&lt;/code&gt; tests.&lt;/li&gt;
&lt;li&gt;Two benchmarks, &lt;code&gt;akka-uct&lt;/code&gt; and &lt;code&gt;db-shootout&lt;/code&gt;, failed to complete on the BPI-F3. Considering the outdated Bianbu OS image, take these RISC-V numbers with a grain of salt.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Performance-wise, the Banana Pi's are a pleasant surprise! The BPI-M4 Zero, the cheapest and smallest board in this whole comparison, if you take the amount of memory and eMMC into account, outran both its direct rival, the Raspberry Pi Zero 2, and the pricier BeagleY-AI on every single benchmark. And the BPI-F3 holds its own on the RISC-V side too, landing right next to the OrangePi RV2 and comfortably ahead of the BeagleV-Fire.&lt;/p&gt;

&lt;p&gt;The rough edges are elsewhere. Documentation is scattered across broken links and outdated OS images, the M4 Zero's header needs to be soldered on yourself (and mine didn't go smoothly!), and Pi4J's GPIO first test still throws exceptions I'll need to dig into further. And unlike the boards themselves, the price is no longer a highlight: the memory chip shortage pushed the prices up, but that's similar for all brands.&lt;/p&gt;

&lt;p&gt;Still, if you can get past the price and the setup hassle, Java, JBang, and even an early-access JDK 25 all run without problems on these boards. I'll keep digging into the Pi4J GPIO issue and share an update once I've got the it working.&lt;/p&gt;

&lt;p&gt;That wraps up this round of my "2026 single-board computer" series! If you're working on similar projects or have experience with Java on Banana Pi (or RISC-V in general), I'd love to hear about it. Feel free to reach out through &lt;a href="https://foojay.social/@frankdelporte" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt; or the &lt;a href="https://foojay.io/" rel="noopener noreferrer"&gt;Foojay.io community&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>bananapi</category>
      <category>singleboardcomputer</category>
      <category>pi4j</category>
    </item>
    <item>
      <title>A New Website for MelodyMatrix, and What's New in Release 1.1.0</title>
      <dc:creator>Frank Delporte</dc:creator>
      <pubDate>Mon, 13 Jul 2026 08:04:34 +0000</pubDate>
      <link>https://dev.to/fdelporte/a-new-website-for-melodymatrix-and-whats-new-in-release-110-4nmo</link>
      <guid>https://dev.to/fdelporte/a-new-website-for-melodymatrix-and-whats-new-in-release-110-4nmo</guid>
      <description>&lt;p&gt;Last week was busy for &lt;a href="https://melodymatrix.rocks/" rel="noopener noreferrer"&gt;MelodyMatrix&lt;/a&gt;. The website got rebuilt from the ground up, and the app itself got a solid release with a handful of new features. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Website Is Completely New
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;melodymatrix.rocks&lt;/code&gt; used to be one Spring Boot + Vaadin Flow application serving everything: the public  pages, the account/admin back office, and the REST API used by the desktop app. That worked, but a Vaadin app isn't a perfect match for a public product site as it's not great for SEO.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;www.melodymatrix.rocks&lt;/code&gt; is now a &lt;strong&gt;static, multilingual Hugo site&lt;/strong&gt;, live in &lt;strong&gt;8 languages&lt;/strong&gt; (English, Dutch, German, French, Italian, Polish, Japanese, Chinese), deployed to GitLab Pages. The Vaadin application still exists, but it's trimmed down to only what actually needs a server: &lt;code&gt;my.melodymatrix.rocks&lt;/code&gt; (account, license, teacher/admin back office) and &lt;code&gt;api.melodymatrix.rocks&lt;/code&gt; (the public REST API used by the desktop app, plus the Polar.sh license sales webhook). At this moment, both live in the same application, but the plan is to split them into separate microservices whenever that becomes necessary, e.g. if we sell thousands of licenses a day ;-) &lt;/p&gt;

&lt;p&gt;The new site pulls in live data at build time instead of duplicating it by hand: a small Python script fetches the current releases and the open-source "thanks" list from the API before every Hugo build, so the &lt;a href="https://melodymatrix.rocks/releases/" rel="noopener noreferrer"&gt;Releases page&lt;/a&gt; and the credits are never out of sync with what's actually shipped. This way it's easy to keep the app and website in sync, without the need to maintain the same data in two places.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1w3jsimg5ex5tsktry0q.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1w3jsimg5ex5tsktry0q.png" alt="Homepage of the new website" width="800" height="711"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Built the Same Way as Lottie4J
&lt;/h2&gt;

&lt;p&gt;If this sounds familiar, it's because it's the &lt;a href="https://webtechie.be/post/closing-the-visual-gap-between-the-official-lottie-webplayer-and-lottie4j/" rel="noopener noreferrer"&gt;same approach I wrote about for Lottie4J&lt;/a&gt; two weeks ago: &lt;strong&gt;Theia IDE&lt;/strong&gt; driving the &lt;strong&gt;Claude API&lt;/strong&gt;, with an &lt;code&gt;@Architect&lt;/code&gt; writing a plan and a &lt;code&gt;@Coder&lt;/code&gt; executing it in its own scoped session to simplify the Vaadin website. The layout for the website was created with Claude Design which exported it flawless into a Hugo template. Moving the content from Vaadin to the new website, the i18n scaffolding for 8 languages, the data-driven feature/view/tier cards, was done with AI-assisted coding. Let's be honest, this is the new way of working. Still, a lot of manual work for testing, reviewing, improving, etc. But most of the coding work was done with and by the tools. Yes, it cost me a few 100 dollars in Claude credits, but it saved me a lot of time and frustration. For a pet project, with only minimal available time, I wouldn't have achieved this in such a short time.&lt;/p&gt;

&lt;p&gt;The same discipline applied: small, well-bounded tasks (one section at a time: navigation, features, downloads, legal pages, translations) instead of one giant prompt, and a fresh session per task rather than one ever-growing conversation. &lt;/p&gt;

&lt;h2&gt;
  
  
  App Release 1.1.0
&lt;/h2&gt;

&lt;p&gt;While the website was being rebuilt, MelodyMatrix itself also moved forward to &lt;strong&gt;&lt;a href="https://melodymatrix.rocks/releases/" rel="noopener noreferrer"&gt;version 1.1.0&lt;/a&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Audio spectrum view&lt;/strong&gt;: a new visualizer added to the set of viewer stages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Falling/rising note blocks&lt;/strong&gt; in the Piano view, closer to the "note highway" style many musicians expect from a practice tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved sync between view, soundfont, and audio&lt;/strong&gt; playback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;i18n support&lt;/strong&gt;, with extra languages added to the desktop app itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved MIDI view&lt;/strong&gt; and reordered views for a more logical order.&lt;/li&gt;
&lt;li&gt;A fixed macOS DMG build step in the release pipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As before, the &lt;a href="https://github.com/codewriterbv/melodymatrix-app-views" rel="noopener noreferrer"&gt;Viewers module stays open source&lt;/a&gt; and the download packages are published through &lt;a href="https://www.jdeploy.com/" rel="noopener noreferrer"&gt;jDeploy&lt;/a&gt; via &lt;a href="https://github.com/codewriterbv/melodymatrix-app-releases/releases" rel="noopener noreferrer"&gt;GitHub Releases&lt;/a&gt;, with the &lt;a href="https://melodymatrix.rocks/download/" rel="noopener noreferrer"&gt;download page on the new site&lt;/a&gt; always pointing at the current build.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fax8vvaq1s4a4ocyfxov9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fax8vvaq1s4a4ocyfxov9.png" alt="Piano with effects view" width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;With the website live and 1.1.0 out, we will be focusing on content improvements. We want to create videos to demonstrate all views and features. And Vik is working on multiple series of lessons that will be integrated in the app to learn to play piano, use jazz chords, etc. &lt;/p&gt;

&lt;p&gt;Please take a look at the new website and install MelodyMatrix (it's free for most features). Let us know your remarks and provide feedback about what's bad/great! If you spot a broken translation, a confusing page, or a bug in the app, issues can be reported in the &lt;a href="https://github.com/codewriterbv/melodymatrix-app-views/issues" rel="noopener noreferrer"&gt;open-source Viewers repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>javafx</category>
      <category>midi</category>
      <category>melodymatrix</category>
    </item>
    <item>
      <title>My Book 'Java Programming for Raspberry Pi' is Now Available as Softcover and Hardcover on Amazon</title>
      <dc:creator>Frank Delporte</dc:creator>
      <pubDate>Fri, 10 Jul 2026 07:20:33 +0000</pubDate>
      <link>https://dev.to/fdelporte/my-book-java-programming-for-raspberry-pi-is-now-available-as-softcover-and-hardcover-on-amazon-590b</link>
      <guid>https://dev.to/fdelporte/my-book-java-programming-for-raspberry-pi-is-now-available-as-softcover-and-hardcover-on-amazon-590b</guid>
      <description>&lt;p&gt;"Java Programming for Raspberry Pi: A Hands-On Guide to Electronics and IoT Projects" is now available as a &lt;strong&gt;softcover and hardcover paper book&lt;/strong&gt; through Amazon, on top of the Kindle ebook and the pay-as-you-wish version on &lt;a href="https://leanpub.com/gettingstartedwithjavaontheraspberrypi" rel="noopener noreferrer"&gt;Leanpub&lt;/a&gt;. If you prefer holding a physical book while you're wiring up LEDs and sensors on your Raspberry Pi, that's now possible again.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it got there
&lt;/h2&gt;

&lt;p&gt;The first version of my book, got published as paper book by Elektor in 2020. At that time, they basically took the Leanpub manuscript, did some minor changes, and printed 1000 copies. As a writer, you get only a few percent of the sales price, so although those 1000 copies sold out, you can still considere it a pet project ;-) After that, Elektor returned me the publishing right for a paper version.&lt;/p&gt;

&lt;p&gt;I'm happy to announce, new paper editions are now available! I exported a non-branded version of the manuscript straight from Leanpub and pushed it through Amazon KDP (Kindle Direct Publishing) as a print-on-demand title. I won't pretend it was a smooth ride: reformatting a Leanpub export into something KDP accepts for interior layout and cover specs took several rounds of trial and error. But it is still the shortest and easiest path I found to get a self-published book into a paper form without signing up for a subscription with a print service. You upload once, and Amazon handles printing, shipping, and returns per order.&lt;/p&gt;

&lt;p&gt;Amazon runs price reductions on both the softcover and hardcover fairly often, so it's worth &lt;a href="https://www.amazon.com/dp/B0GZWWKB1L" rel="noopener noreferrer"&gt;keeping an eye on the listing&lt;/a&gt; if the current price is higher than you want to pay. Also, &lt;strong&gt;make sure to check your local Amazon website to avoid extra import or shipping costs&lt;/strong&gt;!&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxd38wct83wvuuwk4hvwo.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxd38wct83wvuuwk4hvwo.jpg" alt="Paper versions of the book" width="800" height="793"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping it in sync with Leanpub
&lt;/h2&gt;

&lt;p&gt;The Leanpub version is the one I keep actively updating, and I'll try to push those updates through to the Amazon paper editions too. Re-exporting and republishing through KDP is more manual than a Leanpub update, so the paper book will always trail a bit behind.&lt;/p&gt;

&lt;p&gt;That said, the timing works out well. End of 2025 and start of 2026 I did a big rewrite to bring everything up to Java 25 and Pi4J V4. That means this print edition is already current with the state of the ecosystem today, and I don't expect Java, Pi4J, or the Raspberry Pi hardware line to go through equally massive changes again any time soon. So even without frequent paper reprints, this version will stay a solid, up-to-date reference for the next few years, especially if you're someone who just prefers reading from paper instead of a screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to get it
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Softcover (Amazon)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com/dp/B0GZWWKB1L" rel="noopener noreferrer"&gt;amazon.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardcover (Amazon)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com/dp/B0GZWZJSJN" rel="noopener noreferrer"&gt;amazon.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kindle ebook (Amazon)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com/dp/B0GXCF1RQT" rel="noopener noreferrer"&gt;amazon.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ebook, pay-as-you-wish&lt;/td&gt;
&lt;td&gt;&lt;a href="https://leanpub.com/gettingstartedwithjavaontheraspberrypi" rel="noopener noreferrer"&gt;leanpub.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href="https://webtechie.be/books/" rel="noopener noreferrer"&gt;books page on my website&lt;/a&gt; has links for more Amazon regions (UK, DE, JP, CA, BE, NL, ...) and stays the up-to-date overview if these change. The full sources, extra links, and documentation remain free on &lt;a href="https://github.com/FDelporte/JavaOnRaspberryPi" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>raspberrypi</category>
      <category>pi4j</category>
      <category>javaonsingleboardcomputers</category>
    </item>
    <item>
      <title>Closing the Visual Gap Between the Official Lottie Webplayer and Lottie4J</title>
      <dc:creator>Frank Delporte</dc:creator>
      <pubDate>Thu, 02 Jul 2026 09:55:07 +0000</pubDate>
      <link>https://dev.to/fdelporte/closing-the-visual-gap-between-the-official-lottie-webplayer-and-lottie4j-54nn</link>
      <guid>https://dev.to/fdelporte/closing-the-visual-gap-between-the-official-lottie-webplayer-and-lottie4j-54nn</guid>
      <description>&lt;p&gt;A Lottie library is only as good as its output looks. If an animation renders differently in &lt;a href="https://lottie4j.com/" rel="noopener noreferrer"&gt;Lottie4J&lt;/a&gt; than it does in the official web player, that's a bug, even when no exception is thrown and the code looks correct. &lt;/p&gt;

&lt;p&gt;Within the &lt;code&gt;fxfileviewer&lt;/code&gt;, there is an app to visually compare the result of the JavaFX player and a webview using the official JavaScript player. Problem is that I was using the JavaFX Web component for this and this doesn't fully support the latest/best version of this player. Based on this app for manual checks, I also created a unit test which is able to loop over a set of files and compare the differences to make sure changes in the code don't break the existing renderer. But I kept struggling with the same test file &lt;a href="https://github.com/lottie4j/lottie4j/tree/main/fxfileviewer/src/test/resources/json/interactive_mood_selector_ui.json" rel="noopener noreferrer"&gt;interactive_mood_selector_ui.json&lt;/a&gt; which didn't render correctly, both in the JavaFX view and my web-based view to compare it with.&lt;/p&gt;

&lt;p&gt;Over the last weeks the focus has been exactly there: &lt;strong&gt;making the JavaFX output match the reference renderer, pixel for pixel&lt;/strong&gt; (if possible).&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftp4nwivkrhcq6wlbgw78.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftp4nwivkrhcq6wlbgw78.png" alt="Improved result" width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Improved Comparison Workflow
&lt;/h3&gt;

&lt;p&gt;The biggest change is not a rendering fix but the way rendering is now verified. The test suite renders the same animation two ways and compares them frame by frame:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;reference&lt;/strong&gt; side runs the official web player inside a headless Chrome instance. This reference renderer was migrated from &lt;code&gt;lottie-web&lt;/code&gt; to &lt;strong&gt;&lt;code&gt;dotlottie-wc&lt;/code&gt; (thorvg)&lt;/strong&gt;, the engine LottieFiles is standardizing on. The reference images are not generated during the unit test, but as a one-time job on my PC and committed in the &lt;a href="https://github.com/lottie4j/lottie4j/tree/main/fxfileviewer/src/test/resources" rel="noopener noreferrer"&gt;test/resources&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Lottie4J&lt;/strong&gt; side renders the same file through the JavaFX player.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now each frame is diffed, instead of every 5 frames before, and measured against a tolerance "floor". When Lottie4J drifts from the reference, a test fails and points at the exact frame. This runs with headless JavaFX on GitHub Actions as described in the post &lt;a href="https://webtechie.be/post/testing-lottie4j-javafx-animations-in-github-actions-without-a-display-javafx-26-headless-to-the-rescue/" rel="noopener noreferrer"&gt;Testing Lottie4J JavaFX Animations in GitHub Actions Without a Display: JavaFX 26 Headless to the Rescue&lt;/a&gt;. That turned "this animation looks a bit off" into a concrete, reproducible signal. The goal is to get each file to &lt;a href="https://github.com/lottie4j/lottie4j/blob/main/fxfileviewer/src/test/java/com/lottie4j/fxfileviewer/CompareFxViewWithWebViewTest.java#L85" rel="noopener noreferrer"&gt;99.5% similarity&lt;/a&gt;, but that value can be overruled per file. As you can &lt;a href="https://github.com/lottie4j/lottie4j/blob/main/fxfileviewer/src/test/java/com/lottie4j/fxfileviewer/CompareFxViewWithWebViewTest.java#L135" rel="noopener noreferrer"&gt;see in the code&lt;/a&gt;, the "worst" file is now at 95.2%, and yes, it's still that difficult &lt;code&gt;interactive_mood_selector_ui.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;     &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;PER_FILE_FLOOR_OVERRIDE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofEntries&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"json/interactive_mood_selector_ui.json"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;95.2&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
            &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"json/animated_background_patterns.json"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;99.2&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
            &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"json/angry_bird.json"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;98.2&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
            &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"json/face-peeking.json"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;98.5&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
            &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"json/java_duke_flip.json"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;95.7&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
            &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"json/java_duke_slidein.json"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;98.8&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
            &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"json/lottie_lego.json"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;98.0&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
            &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"json/sandy_loading.json"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;99.3&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
            &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dot/demo-1.lottie"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;99.3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;New real-world test animations, like &lt;code&gt;pi4j.json&lt;/code&gt;, &lt;code&gt;foojay-reporter.json&lt;/code&gt; and &lt;code&gt;foojay-duke.json&lt;/code&gt;, got added so the harness measures against the files I'm actually using.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rendering Fixes
&lt;/h3&gt;

&lt;p&gt;With the harness in place, the diffs made it obvious where JavaFX and the web player disagreed. The fixes can be grouped into a few areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Easing&lt;/strong&gt;: The &lt;code&gt;lottie-web&lt;/code&gt; &lt;code&gt;BezierEaser&lt;/code&gt; was ported byte-for-byte into a shared &lt;code&gt;core.helper.BezierEasing&lt;/code&gt;, and the arc renderer now clamps the easing solver to prevent bezier divergence, with a bisection fallback for flat-point curves. A full-circle trim path that used to flicker (floating-point precision loss when wrapping the offset) is fixed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mattes&lt;/strong&gt;: Pixel-level matte composition was extracted into a testable static helper, and the inverted-alpha matte type (&lt;code&gt;tt: 2&lt;/code&gt; → &lt;code&gt;INVERTED_ALPHA&lt;/code&gt;) is now handled correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gradients&lt;/strong&gt;: Alpha and colour stops are merged at the union of their offsets, and linear-RGB gradient stops are densified so colour transitions match the reference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blur and blend modes&lt;/strong&gt;: Gaussian blur was improved, the blend-mode offscreen buffer size is rounded up (ceil) so nothing gets clipped, and the effects renderer was reworked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text&lt;/strong&gt;: Text colour handling and text animation are now correct.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Individually these are small, but together they close a lot of the visible distance between Lottie4J and the official player.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built with Systematic AI Coding
&lt;/h3&gt;

&lt;p&gt;All of this was implemented with &lt;strong&gt;&lt;a href="https://theia-ide.org/" rel="noopener noreferrer"&gt;Theia IDE&lt;/a&gt;&lt;/strong&gt; using the &lt;strong&gt;Claude API&lt;/strong&gt;. I learned about Theia IDE during an Eclipse Foundation workshop in Brussels. I wrote about it on Foojay.io: &lt;a href="https://foojay.io/today/systematic-ai-coding-my-takeaways-from-the-eclipse-foundation-workshop-in-brussels/" rel="noopener noreferrer"&gt;Systematic AI coding, my takeaways&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The takeaways from that article map almost one-to-one onto this batch of work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Design before prompting, then review the output&lt;/strong&gt;: The comparison harness is what makes review possible. And the &lt;code&gt;@Architect&lt;/code&gt; within Theia IDE was able to run the tests file by file, find differences, write plans, and evaluate the results. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A tight feedback loop beats one big prompt&lt;/strong&gt;: Failing frame → targeted fix → re-run → next frame. The &lt;a href="https://github.com/lottie4j/lottie4j/commits/main/" rel="noopener noreferrer"&gt;commit history reads as exactly that rhythm&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep sessions scoped and don't fear a restart&lt;/strong&gt;: The &lt;code&gt;@Architect&lt;/code&gt; writes the plan. And then you start a new session and ask the &lt;code&gt;@Coder&lt;/code&gt; agent to execute it. Each rendering area (mattes, gradients, blur, text) was tackled as its own well-bounded task rather than one ever-lasting conversation. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let the tools review too&lt;/strong&gt;: A round of code improvements came straight out of SonarQube findings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I committed all the tasks written by the &lt;code&gt;@Architect&lt;/code&gt; &lt;a href="https://github.com/lottie4j/lottie4j/tree/main/.prompts/done" rel="noopener noreferrer"&gt;into the repository&lt;/a&gt; so they are available as "history of the project". The most important take-away here isn't that AI wrote the code, but the disciplined process with clear tasks and small iterations, each in a new session.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Result
&lt;/h3&gt;

&lt;p&gt;Where are we now? Check the images below.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The first screenshot is the test file in the &lt;a href="https://lottiefiles.com/preview" rel="noopener noreferrer"&gt;&lt;strong&gt;Preview player&lt;/strong&gt; on the Lottie website&lt;/a&gt;. As you can see a lot of gradients are used in the background and around the emojis. Those are the parts I was struggling with...&lt;/li&gt;
&lt;/ol&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqhdxpcup3obc96j32chq.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqhdxpcup3obc96j32chq.png" alt="Lottie Preview" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The second screenshot is the &lt;strong&gt;"before"&lt;/strong&gt;. Both the JavaFX and the webview version have a lot of differences. This also means I was using the wrong comparison source images to validate the JavaFX result!&lt;/li&gt;
&lt;/ol&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fswbpm6earmhuwvwp31z0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fswbpm6earmhuwvwp31z0.png" alt="Before improvements" width="799" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The third screenshot is the &lt;strong&gt;"after"&lt;/strong&gt; which shows a lot of improvements. First and most important, the comparison images from the webview are now pixel-perfect as they get rendered based on the latest version of the official Lottie web player. And you can also see that the JavaFX result is now a really close match! The background gradients are not pixel-perfect yet, and I still see small differences in the text, which confirms the 95% similarity score. But overall, I'm very happy with the improvements! &lt;/li&gt;
&lt;/ol&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2kq48e6aahy80b7ktwqo.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2kq48e6aahy80b7ktwqo.png" alt="After improvements" width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Cost
&lt;/h3&gt;

&lt;p&gt;Of course, the AI coding process is not free. Theia IDE luckily is free, but it needs one or more API keys to call AI services. As you can see, I burned a lot of tokens, and budget. But to be honest, I would not have achieved these improvements by myself in such a short time. Actually, I could do my "real work", and have the tools work in the background!&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fie8q3a8fj7hbrvozcyt0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fie8q3a8fj7hbrvozcyt0.png" alt="Screenshot Claude cost" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;I'm happy with the results so will release a new version soon. The same approach with &lt;code&gt;@Architect&lt;/code&gt;/&lt;code&gt;@Coder&lt;/code&gt; will help me to further improve or fix the library when needed. But first I want to know which visual differences remain based on feedback from the users of the Lottie4J library!&lt;/p&gt;

&lt;p&gt;As always, contributions, bug reports, and pull requests are welcome at the &lt;a href="https://github.com/lottie4j/lottie4j" rel="noopener noreferrer"&gt;Lottie4J GitHub repository&lt;/a&gt;. If you have an animation that renders differently than you expect, isolate it, and open an issue with the JSON. That's exactly the kind of case the test flow is built to catch.&lt;/p&gt;

</description>
      <category>java</category>
      <category>javafx</category>
      <category>lottie</category>
      <category>animation</category>
    </item>
    <item>
      <title>From breadboard chaos to a real PCB: designing the Pi4J smoke test board</title>
      <dc:creator>Frank Delporte</dc:creator>
      <pubDate>Thu, 25 Jun 2026 09:30:36 +0000</pubDate>
      <link>https://dev.to/fdelporte/from-breadboard-chaos-to-a-real-pcb-designing-the-pi4j-smoke-test-board-538g</link>
      <guid>https://dev.to/fdelporte/from-breadboard-chaos-to-a-real-pcb-designing-the-pi4j-smoke-test-board-538g</guid>
      <description>&lt;p&gt;Testing a Java I/O library properly means testing it on real hardware. No mocks, no stubs, just actual pins doing actual things. For Pi4J, that means running the smoke test: a setup with two BMP/BME280 sensors, some GPIO-to-GPIO jumper connections, and a bunch of patience while you untangle the wires for the third time this week.&lt;/p&gt;

&lt;p&gt;That last part is what finally pushed us to design a proper PCB. This is the story of board number &lt;code&gt;0001&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/q7SGdxQnMVc"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Pi4J smoke test
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.pi4j.com/hardware-testing/hardware-testing/" rel="noopener noreferrer"&gt;Pi4J smoke test&lt;/a&gt; is a test project that verifies the Pi4J library works correctly on real Raspberry Pi hardware. It covers the most important communication protocols:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I2C: using a BMP280 or BME280 sensor for air pressure and temperature&lt;/li&gt;
&lt;li&gt;SPI: a second BMP/BME280 sensor over SPI&lt;/li&gt;
&lt;li&gt;GPIO: several pin-to-pin connections to test PWM output, digital output, digital input, and debounce behaviour&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The wiring involves quite a few connections. Two sensors, a T-cobbler breakout board plugged into a breadboard, and a collection of colour-coded jumper wires connecting GPIO pins to each other. It works fine, but "works fine" and "reliable test setup" are not always the same thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with the breadboard
&lt;/h2&gt;

&lt;p&gt;Every time someone of the Pi4J sets up the smoke test on a breadboard, something is slightly different. A wire in the wrong column. A jumper that looks seated but is not. A sensor that falls over when you move the board.&lt;/p&gt;

&lt;p&gt;This is fine for a quick experiment. It is not great when you want to hand the setup to a contributor, or use it consistently across different hardware revisions of the Raspberry Pi. The goal of a smoke test is to give you a fast, repeatable answer to the question "does this still work". A breadboard that changes shape every session works against that goal.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Femwe2ggylvat67bmsdrk.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Femwe2ggylvat67bmsdrk.jpg" alt="Smoketest setup on a breadboard" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we wanted to have a "fixed solution" already for a long time...&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the board with EasyEDA
&lt;/h2&gt;

&lt;p&gt;I used &lt;a href="https://pro.easyeda.com/editor" rel="noopener noreferrer"&gt;EasyEDA Pro&lt;/a&gt; for the design. It is a browser-based PCB design tool with a built-in schematic editor, a large component library, and direct integration with JLCPCB for ordering. The learning curve is manageable, especially for a board this straightforward.&lt;/p&gt;

&lt;p&gt;I did not do this alone. Jan, a coach from my CoderDojo club in Ieper, helped me convert the breadboard setup into a proper schematic. That turned out to be the right call. There are details in PCB design, like how to connect the sensors, adding the LEDs, component placement, and clearance rules, where experience matters more than enthusiasm. Jan has a lot of hardware-design experience and was happy to share it.&lt;/p&gt;

&lt;p&gt;The starting point was the wiring tables from the smoke test documentation. Every connection in those tables needed to end up as a trace or a connector on the board. That gave us a clear scope and made the design process straightforward.&lt;/p&gt;

&lt;p&gt;In the schematic you don't have to draw every wire as a line. Instead you use net labels: a pin gets a label like &lt;code&gt;SDA&lt;/code&gt; or &lt;code&gt;SCL&lt;/code&gt;, and every other pin with the same label is connected to it. The same approach works for both the I2C and the SPI side, and for the ground and 3.3V connections that run across the board. It keeps the schematic readable while still describing every single connection from the wiring table.&lt;/p&gt;

&lt;p&gt;From the schematic, EasyEDA can propose a board layout for you. That proposal is only a starting point. I did a lot of fine-tuning on top of it and went back and forth with the Pi4J team before the layout was right. A nice feature here is that you can hover over a net and watch all its connections light up, which makes it easy to validate that, for example, the ground plane and the SPI wires really go where they should. EasyEDA also has a 3D visualization, so you can turn the board around, check it from all angles, and confirm that everything is labeled correctly before ordering anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is on the board
&lt;/h2&gt;

&lt;p&gt;The finished board has a few distinct sections.&lt;/p&gt;

&lt;p&gt;The 40-pin GPIO header runs across the middle, with pin numbers printed on both rows. This connects directly to the Raspberry Pi GPIO header. Above the header, a row of SMD resistors and LEDs helps to see if the GPIO-to-GPIO tests are running. They are grouped by the pin ranges they cover: 12-16, 13-15, 16-22, 32-36, 36-37, and 38. Each group maps to one of the test cases in the smoke test wiring table.&lt;/p&gt;

&lt;p&gt;On the left side of the board there is an I2C connector for the first BMP/BME280 sensor. On the right side there is an SPI connector for the second sensor. &lt;/p&gt;

&lt;p&gt;The board also has mounting holes in all four corners, which means you can actually fix it to something instead of letting it slide around on a desk. And there are two additional 20-pin headers so it's easy to connect a measuring instrument if something seems to be not working as expected.&lt;/p&gt;

&lt;p&gt;Board number 0001 is the first one off the production run. The QR code in the top corner is the PCB manufacturer's tracking code.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn0c74nzkn38hhfr0vjbi.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn0c74nzkn38hhfr0vjbi.jpg" alt="PCB 001" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ordering the board
&lt;/h2&gt;

&lt;p&gt;The board is manufactured by JLCPCB, which integrates directly with EasyEDA. This is where I ran into some user experience problems. These companies are great at hardware, but the ordering software is confusing the first time you use it. From the schematic view, the only thing you get is an "order the parts" button that drops you on the JLCPCB website without actually starting an order. The real ordering options live in the PCB view, behind a different set of buttons that are not clearly labeled. I ended up chatting with the support team just to figure out which button I needed to press.&lt;/p&gt;

&lt;p&gt;Once I found the right place, ordering was easy. The minimum quantity is five boards, which was fine, because I also wanted them assembled and will share with members of the Pi4J team. The only component not in stock was the 40-pin GPIO header, but that is cheap and easy to solder yourself, so it was not a problem.&lt;/p&gt;

&lt;p&gt;The package arrived well protected, with each board resting on a piece of foam to protect the headers. The boards came on a larger panel because my design was too small to run through the production machines on its own, so the manufacturer adds extra board material around it. After that it was just a matter of soldering the 40-pin header, which goes straight through and is the easy part.&lt;/p&gt;

&lt;h2&gt;
  
  
  First results
&lt;/h2&gt;

&lt;p&gt;To make sure nothing had changed in the code, I first ran the existing smoke test on my old breadboard setup with a Raspberry Pi 5, straight from IntelliJ. The run executes about ten tests across the different protocols, including PWM, digital I/O, I2C, and SPI, and the summary reported all ten passing.&lt;/p&gt;

&lt;p&gt;Then I connected the new PCB using a flat cable, carefully matching pin 1 on the Raspberry Pi to pin 1 on the board. Using two flat cables means I don't have to keep unplugging connectors directly on the Raspberry Pi, which limits the stress on the Pi's own header. Running the same smoke test, the LEDs started blinking and jumping from one pin group to the next as each test ran, a nice visual confirmation that something is actually happening. And the result was the same: all tests passed.&lt;/p&gt;

&lt;p&gt;The connections are solid, the connectors are in the right place, and nothing falls over when you pick it up. That is exactly what you want from a test board.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Next
&lt;/h2&gt;

&lt;p&gt;The plan is to open source the EasyEDA design files and add the board to the Pi4J hardware testing documentation. If there is interest from contributors, it could become an official Pi4J testing accessory, something you can order yourself or build from the files.&lt;/p&gt;

&lt;p&gt;If you have feedback on the design, or ideas for what else should go on a future revision, open a discussion on the &lt;a href="https://github.com/Pi4J/pi4j/discussions" rel="noopener noreferrer"&gt;Pi4J GitHub&lt;/a&gt; or find us on &lt;a href="https://join.slack.com/t/pi4j/shared_invite/zt-1ttqt8wgj-E6t69qaLrNuCMPLiYnBCsg" rel="noopener noreferrer"&gt;Slack&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And yes, CoderDojo coaches know more about PCB design than you might expect.&lt;/p&gt;

</description>
      <category>iot</category>
      <category>java</category>
      <category>showdev</category>
      <category>testing</category>
    </item>
    <item>
      <title>JavaFX In Action #27 with David Gutierrez about JMathAnim to Create Mathematical Animations</title>
      <dc:creator>Frank Delporte</dc:creator>
      <pubDate>Thu, 25 Jun 2026 09:28:01 +0000</pubDate>
      <link>https://dev.to/fdelporte/javafx-in-action-27-with-david-gutierrez-about-jmathanim-to-create-mathematical-animations-25df</link>
      <guid>https://dev.to/fdelporte/javafx-in-action-27-with-david-gutierrez-about-jmathanim-to-create-mathematical-animations-25df</guid>
      <description>&lt;p&gt;The &lt;a href="https://www.jfx-central.com/links" rel="noopener noreferrer"&gt;JFX Central Links Of The Week&lt;/a&gt; is a great source to discover what is happening in the JavaFX world. One of the projects that caught my eye is JMathAnim, a tool to create mathematical animations built on top of JavaFX. In this interview, David Gutierrez, a mathematician from Spain, walks us through what the project can do and how it came to life.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/pb54youm6AM"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  About David
&lt;/h2&gt;

&lt;p&gt;David Gutierrez is a mathematician who started the JMathAnim project during the COVID lockdown period. By his own admission, he is not a trained developer. He built JMathAnim because he needed a tool that didn't exist in Java yet, and Java was the language he was most comfortable with. He was inspired by &lt;a href="https://www.manim.community/" rel="noopener noreferrer"&gt;Manim&lt;/a&gt;, the popular Python library used to create mathematical animations, and decided to build his own equivalent in Java.&lt;/p&gt;

&lt;p&gt;His work is a great example of what happens when domain expertise meets just enough programming knowledge: the result is a highly focused, purpose-built tool that does one thing very well. You can follow his work and find examples of his animations on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://davidgutierrezrubio.codeberg.page/jmathanim/" rel="noopener noreferrer"&gt;JMathAnim website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codeberg.org/davidgutierrezrubio/jmathanim" rel="noopener noreferrer"&gt;JMathAnim source code on Codeberg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bsky.app/profile/davidcalculin.bsky.social/post/3mlbvm4rlg22d" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About JMathAnim
&lt;/h2&gt;

&lt;p&gt;JMathAnim is a Java library and UI built on JavaFX that lets you create animated mathematical visualizations. The goal is simple: make it easy to build videos that explain mathematical concepts through animation. It's inspired by the kind of content you see on YouTube channels like &lt;a href="https://www.youtube.com/@3blue1brown" rel="noopener noreferrer"&gt;3Blue1Brown&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What It Can Do
&lt;/h3&gt;

&lt;p&gt;The library covers a wide range of use cases. You can animate LaTeX-rendered formulas, morphing one expression into another step by step. You can build geometric visualizations, simulate cell growth, generate fractals, and produce videos explaining number theory concepts like base conversion. Everything you see in the animation is defined in code, and the output can be exported directly to a video file, no intermediate steps required.&lt;/p&gt;

&lt;p&gt;A particularly nice touch is the built-in code editor, which includes syntax highlighting via &lt;a href="https://github.com/bobbylight/rsyntaxtextarea" rel="noopener noreferrer"&gt;RSyntaxTextArea&lt;/a&gt; and code completion hints. This makes it accessible to people who want to experiment with the library without setting up a full Java IDE.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnkqig2hjstp1m4k27cr9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnkqig2hjstp1m4k27cr9.png" alt="Base 10 to 5 animation" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Ruby in the Code Editor?
&lt;/h3&gt;

&lt;p&gt;One interesting design choice is the use of Ruby as the scripting language inside the built-in editor. The reason isn't mathematical tradition, but it's practical. Ruby's scripting model fits well with the way animation sequences are written, making it a natural fit for the kind of short, expressive scripts you need to define animations. The Java library itself handles all the heavy lifting; Ruby is just the glue layer in the interactive editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  JavaFX as the Rendering Engine
&lt;/h3&gt;

&lt;p&gt;All rendering in JMathAnim is done on a JavaFX Canvas. LaTeX formula rendering is handled by &lt;a href="https://github.com/opencollab/jlatexmath" rel="noopener noreferrer"&gt;JLatexMath&lt;/a&gt;, and video export relies on &lt;a href="https://github.com/bytedeco/javacv" rel="noopener noreferrer"&gt;JavaCV&lt;/a&gt;. The combination gives JMathAnim a capable pipeline from code to finished video.&lt;/p&gt;

&lt;p&gt;David is the first to admit that JavaFX had a learning curve, especially since he came at it as a mathematician rather than a developer. But his experience shows that JavaFX is approachable enough that someone without a formal software engineering background can build something genuinely impressive with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Explaining Math Visually
&lt;/h3&gt;

&lt;p&gt;One of the most compelling examples shown during the interview is a visualization explaining the conversion from base 10 to base 5. It's a simple concept, but seeing it animated makes it click in a way that a textbook explanation often doesn't. This is exactly the philosophy behind JMathAnim and the channels that inspired it: a well-made animation can communicate in seconds what would take paragraphs to describe in writing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;JMathAnim:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://davidgutierrezrubio.codeberg.page/jmathanim/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codeberg.org/davidgutierrezrubio/jmathanim" rel="noopener noreferrer"&gt;Source code on Codeberg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bsky.app/profile/davidcalculin.bsky.social/post/3mlbvm4rlg22d" rel="noopener noreferrer"&gt;Video example on Bluesky&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Libraries used:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/bobbylight/rsyntaxtextarea" rel="noopener noreferrer"&gt;RSyntaxTextArea&lt;/a&gt;: Syntax-highlighting text editor component for Java&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/opencollab/jlatexmath/" rel="noopener noreferrer"&gt;JLatexMath&lt;/a&gt;: LaTeX rendering for Java&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/bytedeco/javacv" rel="noopener noreferrer"&gt;JavaCV&lt;/a&gt;: Java interface to OpenCV and FFmpeg for video export&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Inspired by:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.manim.community/" rel="noopener noreferrer"&gt;Manim&lt;/a&gt;: Python library for mathematical animations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;YouTube channel mentioned:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/@3blue1brown" rel="noopener noreferrer"&gt;3Blue1Brown&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Video content
&lt;/h2&gt;

&lt;p&gt;00:00 Who is David&lt;/p&gt;

&lt;p&gt;00:33 Goal of JMathAnim&lt;/p&gt;

&lt;p&gt;01:16 Demo of JMathAnim&lt;/p&gt;

&lt;p&gt;04:53 Different ways to create animations in the tool&lt;/p&gt;

&lt;p&gt;05:40 Why Ruby is used in the code editor&lt;/p&gt;

&lt;p&gt;06:24 More demos&lt;/p&gt;

&lt;p&gt;07:16 Demos and info on the website&lt;/p&gt;

&lt;p&gt;11:12 JavaFX learning curve&lt;/p&gt;

&lt;p&gt;13:46 Libraries used in the project&lt;/p&gt;

&lt;p&gt;14:30 Example to explain base 10 to base 5 conversion&lt;/p&gt;

&lt;p&gt;16:27 Conclusion&lt;/p&gt;

&lt;h2&gt;
  
  
  More JFX In Action...
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://webtechie.be/tags/jfx-in-action/" rel="noopener noreferrer"&gt;Click here for more posts with JFX In Action videos&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>javafx</category>
      <category>mathematics</category>
      <category>programming</category>
    </item>
    <item>
      <title>Systematic AI Coding: My Takeaways from the Eclipse Foundation Workshop in Brussels</title>
      <dc:creator>Frank Delporte</dc:creator>
      <pubDate>Wed, 24 Jun 2026 08:24:03 +0000</pubDate>
      <link>https://dev.to/fdelporte/systematic-ai-coding-my-takeaways-from-the-eclipse-foundation-workshop-in-brussels-5dd8</link>
      <guid>https://dev.to/fdelporte/systematic-ai-coding-my-takeaways-from-the-eclipse-foundation-workshop-in-brussels-5dd8</guid>
      <description>&lt;p&gt;Most developers using AI tools are still guessing. The Eclipse Foundation's first &lt;a href="https://aieclipse.org/ai-workshop/" rel="noopener noreferrer"&gt;AI Coding Workshop&lt;/a&gt; in Brussels was built to change that. It's a brand new format they launched in Brussels, which makes sense: most of the Eclipse event team is based there, right in the heart of Europe. The plan is to take it to more cities from here, so keep an eye out if you want to attend such a workshop in the future. They offered 10 free tickets to share with the BeJUG and Foojay community. And in all honesty, I used one for myself ;-). &lt;a href="https://www.linkedin.com/in/jonas-helming-76303b28/" rel="noopener noreferrer"&gt;Jonas Helming&lt;/a&gt; from &lt;a href="https://eclipsesource.com/" rel="noopener noreferrer"&gt;EclipseSource&lt;/a&gt;  led the workshop, with a fun quiz hosted by &lt;a href="https://www.linkedin.com/in/tfroment/" rel="noopener noreferrer"&gt;Thomas Froment&lt;/a&gt; in the afternoon. Here's what I took away.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnb2k80dm9vsvwt4useuz.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnb2k80dm9vsvwt4useuz.jpg" alt="Jonas Helming in action" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  From Vibe to Systematic
&lt;/h2&gt;

&lt;p&gt;The workshop kicked off with an important framing question: where are we in the AI coding journey? Jonas positioned AI assistance on a scale from 1 (basic code suggestions) to 5 (fully autonomous). We are now at level 4 with tools like Claude Code. Level 5 is approaching when fully cloud-based autonomous agents will be available.&lt;/p&gt;

&lt;p&gt;Most developers today are still somewhere in the "vibe coding" zone. Vibe coding is the &lt;em&gt;what, not how&lt;/em&gt; approach: you describe what you want, don't look at the generated code too closely (or not all), and focus on the result. It can work well for POCs and low-complexity problems on well-understood domains. But it quickly crumbles once you move into real projects with real complexity.&lt;/p&gt;

&lt;p&gt;The fundamental shift the workshop is about, is moving beyond the experimentation phase toward something repeatable and systematic. That's what Jonas calls &lt;em&gt;Systematic AI Coding&lt;/em&gt;: understanding how coding agents actually work, and building workflows around them, not just prompting and hoping.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a Coding Agent Actually Works
&lt;/h2&gt;

&lt;p&gt;One of the most useful sessions was the breakdown of what happens inside a coding agent. It's easy to think of it as a magic box, but the mechanics matter a lot for using it well.&lt;/p&gt;

&lt;p&gt;A coding agent takes several inputs: the system message, user messages, assistant responses, your codebase, and any additional context you provide. That context includes things like project context, task context (your plan), and the skills or tools available to the agent. On top of that, you can have optional agent layering, like a model router that picks the right model for the task.&lt;/p&gt;

&lt;p&gt;From there, the agent uses tools, calls the LLM, and generates a change set: files, actions, context updates, MCP calls. Everything flows through what Jonas describes as part of the &lt;em&gt;Request Model&lt;/em&gt;: tools + messages + tool messages + tool responses. The whole thing boils down to one word: &lt;strong&gt;CONTEXT&lt;/strong&gt;. Context is everything.&lt;/p&gt;

&lt;p&gt;A key insight: &lt;strong&gt;the LLM is stateless and forgets immediately.&lt;/strong&gt; Every time it runs, you need to resend the full conversation history for it to understand what happened before. This has a real consequence as the context gets polluted and the cost rises because extra tokens are needed to handle the input. And there's a subtle trap called the "dumb zone": efficiency actually drops before you hit the context window limit. According to some experiments, this can already start at roughly ±40% of the context capacity. So a session that feels like it's still working might already be producing worse results.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Workflow
&lt;/h2&gt;

&lt;p&gt;Once you understand how agents work, the practical workflow makes a lot more sense. It looks roughly like this:&lt;/p&gt;

&lt;p&gt;Your AI Agent generates output, and you need to &lt;strong&gt;Review or Decide&lt;/strong&gt;. From there you have several options: escape (abort), refine (adjust the prompt), redo (start a fresh chat), divide the task further, compact the session, adjust the agent configuration, or commit what's working. Then repeat.&lt;/p&gt;

&lt;p&gt;Two important take-aways from Jonas here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't be afraid to throw away changes and start over.&lt;/strong&gt; A fresh session with a cleaner, better-scoped prompt will often outperform trying to rescue a session that's gone sideways. The bad feeling you may have of "lost coding work" is real, but irrelevant as it's not your work, but done by a tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compact the session yourself, don't rely on the agent to do it.&lt;/strong&gt; If you let the agent compact automatically, you lose control over what context gets dropped. Doing it yourself means you decide what's kept.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One tip I should follow: when your AI tool is working, don't start another session in another project. Look out the window and think about the actual problem you're solving within the project to better handle the result. Context-switching is expensive for humans too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time Changes When You Use AI
&lt;/h2&gt;

&lt;p&gt;Without AI, a developer typically spends roughly 70% of time on design and coding combined, and 30% on debugging. With AI, the split looks very different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design: ~30%&lt;/li&gt;
&lt;li&gt;Review: ~30%&lt;/li&gt;
&lt;li&gt;Debugging: ~35%&lt;/li&gt;
&lt;li&gt;Actual AI-generated code: ~5%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The work doesn't disappear, but it shifts. You spend more time thinking, reviewing, and debugging, and less time typing code. This has implications for how you approach a task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design&lt;/strong&gt; before you ever prompt the agent:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Think about the task and if it should be split&lt;/li&gt;
&lt;li&gt;Describe your intent with precision&lt;/li&gt;
&lt;li&gt;Provide examples, relevant files, and existing implementations&lt;/li&gt;
&lt;li&gt;Give the agent a way to validate its own output&lt;/li&gt;
&lt;li&gt;Apply the "good friend" metaphor: share the tips and tricks from your own codebase that you'd tell a new colleague&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Review&lt;/strong&gt; isn't optional:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, find blockers&lt;/li&gt;
&lt;li&gt;Decide the follow-up action (refine, redo, split, etc.)&lt;/li&gt;
&lt;li&gt;Only if there are no remaining blockers: do a detailed review to finish the task
## Done ≠ Ready&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When an agent says it's done, that's not a green light to push. AI-generated code can drift on quality, introduce security risks, and produce "work-slop" that frustrates your coworkers. Before pushing, do a review the way a careful coworker would.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Companies Need to Enable This
&lt;/h2&gt;

&lt;p&gt;Jonas highlighted a few important requirements that a company needs to provide to its developers if it wants to make systematic AI coding actually happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tools&lt;/strong&gt; (which cost money)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time&lt;/strong&gt; to learn and experiment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Space&lt;/strong&gt; to experiment and share learnings across the team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That third one is often underrated. Individual experimentation is great, but if there's no mechanism to share what works, the team never levels up together.&lt;/p&gt;

&lt;h2&gt;
  
  
  On the Eclipse Side
&lt;/h2&gt;

&lt;p&gt;There was also a short quiz by Thomas Froment that covered some Eclipse Foundation projects worth knowing about. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open VSX&lt;/strong&gt; is an open-source, vendor-neutral alternative to the Visual Studio Marketplace: an &lt;a href="https://open-vsx.org/" rel="noopener noreferrer"&gt;open registry&lt;/a&gt;, an &lt;a href="https://projects.eclipse.org/projects/ecd.openvsx" rel="noopener noreferrer"&gt;open-source project&lt;/a&gt;, and a working group. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://theia-ide.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;Theia IDE&lt;/strong&gt;&lt;/a&gt; built on the &lt;a href="https://theia-ide.org/theia-platform/" rel="noopener noreferrer"&gt;Theia Platform&lt;/a&gt;, ## a modern, AI-native IDE for cloud and desktop. It has many handy AI-tools for developers, like showing every step an agent takes, token usage, a &lt;code&gt;@PR Review&lt;/code&gt; agent that you can give a PR number to checkout, build, run, and create a review plan, etc.
## Final Thought&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks to the Eclipse Foundation for the free tickets, for the perfect organization, and to everyone from BeJUG and Foojay who joined! This was their first workshop in this format, and I hope they bring it to many more cities soon. Also thanks to the sponsor as they allowed to have a good location with a pro tech team and good food during the whole day.&lt;/p&gt;

&lt;p&gt;It was a dense, practical day. This summary is only a small part of all the tips, tricks, hints, pitfalls, and all the other knowledge shared by Jonas, in over four hours. He also has a longer version of this workshop that he gives at companies and other events. If you have the opportunity to meet him or join one of those sessions, you should definitely do!&lt;/p&gt;

&lt;p&gt;The biggest shift for me wasn't any single tip but the importance of shifting from &lt;strong&gt;vibe&lt;/strong&gt; to &lt;strong&gt;systematic&lt;/strong&gt; coding. AI coding isn't magic and it isn't a shortcut. It's a different kind of engineering discipline, with its own workflows, its own failure modes, and its own learning curve. The developers who benefit most are the ones who take it seriously, build repeatable processes, and keep their engineering judgment front and center.&lt;/p&gt;

&lt;h2&gt;
  
  
  This Post Was Co-Authored With Claude Code
&lt;/h2&gt;

&lt;p&gt;Jonas also highlighted that you can use AI tools for much more than coding alone. So, again in all honesty, Claude Code wrote the initial version of this blog during my train ride from Brussels back to Kortrijk. The tool read my handwritten reMarkable notes, exported as a PDF. But, of course, with a full review, rewrite, and fine-tuning by yours truly :-).&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>llm</category>
      <category>developers</category>
    </item>
    <item>
      <title>A Fresh Design for webtechie.be</title>
      <dc:creator>Frank Delporte</dc:creator>
      <pubDate>Wed, 17 Jun 2026 11:48:22 +0000</pubDate>
      <link>https://dev.to/fdelporte/a-fresh-design-for-webtechiebe-3o45</link>
      <guid>https://dev.to/fdelporte/a-fresh-design-for-webtechiebe-3o45</guid>
      <description>&lt;p&gt;My website grew over many years into more than 260 posts, 220 videos, 100 podcasts, and 60 presentations. The old design buried most of that under a layout that no longer matched the way I write today. So I rebuilt it from the ground up, and the result puts the content first.&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="https://webtechie.be" rel="noopener noreferrer"&gt;webtechie.be&lt;/a&gt;, and you land on a cleaner home page that points you straight to the blog posts, podcasts, and my book. Those are things I spend most time on and deserve more attention. The new design also gives more space to the projects I work on, like Lottie4J, Pi4J, MelodyMatrix, and more. The old site had a lot of content but no clear way to find it. The new one makes it easier to discover what I have to share.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;p&gt;The redesign touches almost every part of the site. These are the changes you notice first.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A new theme&lt;/strong&gt;: The whole site moves to a terminal-inspired look that fits the topics I cover: Java, JavaFX, Raspberry Pi, and single-board computers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More focus on &lt;a href="https://dev.to/posts"&gt;blog posts&lt;/a&gt; and &lt;a href="https://dev.to/podcasts"&gt;podcasts&lt;/a&gt;&lt;/strong&gt;: The home page and navigation push the long-form content forward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A light and dark switch&lt;/strong&gt;: You pick the mode you prefer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A search box&lt;/strong&gt;: Find any post by keyword without scrolling through archive pages, using a redirect to DuckDuckGo's search engine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A table of contents&lt;/strong&gt;: Longer articles show a table of contents, so you jump straight to the section you need.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better use of &lt;a href="https://dev.to/tags"&gt;tags&lt;/a&gt;&lt;/strong&gt;: The tags got reviewed and have a short introduction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More RSS feeds&lt;/strong&gt;: Each section and tag exposes its own feed, so you follow only the parts that interest you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href=""&gt;Open source projects&lt;/a&gt;&lt;/strong&gt;: Highlighted projects that I work on, like Lottie4J, Pi4J, MelodyMatrix, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of these, the redesign carries dozens of smaller tweaks to typography, spacing, images, table layouts, and the metadata that drives social sharing.&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%2Fad120sgubnw91paux3v8.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%2Fad120sgubnw91paux3v8.png" alt="New version with the dark theme" width="800" height="892"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How Claude Code helped
&lt;/h2&gt;

&lt;p&gt;I built a large part of this redesign together with &lt;a href="https://www.claude.com/product/claude-code" rel="noopener noreferrer"&gt;Claude Design and Code&lt;/a&gt;. It handled the repetitive Hugo template work, generated the new theme partials, and helped me track down the small details that make a site feel finished. One example: the social preview images now resolve correctly for every page, which used to break when I shared a link on LinkedIn.&lt;/p&gt;

&lt;p&gt;Pairing with an AI assistant on this kind of project changes the pace. I describe what I want, Claude Code writes the template, and I review the result and adjust. The work that used to take an evening of fiddling now takes a focused hour.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tell me what you think
&lt;/h2&gt;

&lt;p&gt;The site keeps the same address and all the old links still work, so nothing you bookmarked should be broken. Take a look at the new &lt;a href="https://webtechie.be" rel="noopener noreferrer"&gt;webtechie.be&lt;/a&gt;, try the search box and the dark mode, and let me know what works for you and what does not. Your feedback helps me to further improve!&lt;/p&gt;

</description>
      <category>hugo</category>
      <category>blog</category>
      <category>claude</category>
    </item>
    <item>
      <title>Lottie4J Meets LottieFiles: A Conversation with Naail Abdul Rahman</title>
      <dc:creator>Frank Delporte</dc:creator>
      <pubDate>Thu, 04 Jun 2026 09:27:07 +0000</pubDate>
      <link>https://dev.to/fdelporte/lottie4j-meets-lottiefiles-a-conversation-with-naail-abdul-rahman-1gd3</link>
      <guid>https://dev.to/fdelporte/lottie4j-meets-lottiefiles-a-conversation-with-naail-abdul-rahman-1gd3</guid>
      <description>&lt;p&gt;Lottie animations run on Android, iOS, and the web. Getting them working on the JVM is a different story. &lt;a href="https://lottie4j.com" rel="noopener noreferrer"&gt;Lottie4J&lt;/a&gt; started as a question: can JavaFX render them without a WebView? That question turned into a library with a first release in March 2026! Since then, I received the first pull requests, recently added headless unit testing with JavaFX 26, and now this: a video conversation with Naail from the LottieFiles team.&lt;/p&gt;

&lt;p&gt;I got to sit down with &lt;a href="https://www.linkedin.com/in/kudanai/" rel="noopener noreferrer"&gt;Naail Abdul Rahman&lt;/a&gt;, R&amp;amp;D engineer at LottieFiles, the company behind the Lottie animation platform. We talked about where the format came from, where it is going, and what that means for a Java implementation like Lottie4J.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/R6k4NZ_SQqU"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What we talked about
&lt;/h2&gt;

&lt;p&gt;The conversation covers a lot, so here is a quick overview of the topics if you want to jump to a specific part:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;00:00 Introduction of Lottie and Naail&lt;/li&gt;
&lt;li&gt;04:57 Use cases for Lottie animations, e.g. sprites in games, on ESP32,...&lt;/li&gt;
&lt;li&gt;06:21 History of the Android Airbnb player&lt;/li&gt;
&lt;li&gt;08:07 About Lottie4J, the Java(FX) player&lt;/li&gt;
&lt;li&gt;10:31 Nice Lottie animation examples&lt;/li&gt;
&lt;li&gt;14:44 Early history of Lottie, started as Bodymovin&lt;/li&gt;
&lt;li&gt;15:54 .json Lottie versus .zip dotLottie formats&lt;/li&gt;
&lt;li&gt;18:24 "Example Drive Development" of Lottie4J and headless unit testing with JavaFX 26&lt;/li&gt;
&lt;li&gt;19:40 About the Lottie Animation Community (LAC)&lt;/li&gt;
&lt;li&gt;22:43 Lottie format documentation with live examples&lt;/li&gt;
&lt;li&gt;24:52 Taking a look at the web viewer and other tools on the LottieFiles website&lt;/li&gt;
&lt;li&gt;27:20 What the LottieFiles Team Plan offers&lt;/li&gt;
&lt;li&gt;29:07 Lottie animations can be exported from After Effects and Figma&lt;/li&gt;
&lt;li&gt;30:07 Experiments with AI to generate animations&lt;/li&gt;
&lt;li&gt;32:36 LottieFiles Marketspace with free available animations&lt;/li&gt;
&lt;li&gt;33:26 What should be the next steps for Lottie4J?&lt;/li&gt;
&lt;li&gt;35:50 What's on the LottieFiles roadmap&lt;/li&gt;
&lt;li&gt;39:38 Can Lottie4J be validated to be compliant with the community specification?&lt;/li&gt;
&lt;li&gt;41:47 Growing interest in Lottie for desktop applications (KDE)&lt;/li&gt;
&lt;li&gt;42:53 Where to start if you're interested in Lottie&lt;/li&gt;
&lt;li&gt;44:03 Pull requests received for Lottie4J&lt;/li&gt;
&lt;li&gt;47:34 Looking into a pending Lottie4J issue&lt;/li&gt;
&lt;li&gt;49:04 JavaFX gains more popularity as desktop applications become more important in AI use&lt;/li&gt;
&lt;li&gt;50:05 Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lottie: from Bodymovin to everywhere
&lt;/h2&gt;

&lt;p&gt;One thing I always find interesting about the Lottie format is that it did not start at LottieFiles. It started as &lt;a href="https://aescripts.com/bodymovin/" rel="noopener noreferrer"&gt;Bodymovin&lt;/a&gt;, an After Effects plugin for exporting animations to the web. Airbnb picked it up, built their Android player around it, and the name Lottie stuck. From there the format spread across platforms, communities, and now also into Java.&lt;/p&gt;

&lt;p&gt;The open-source specification lives at &lt;a href="https://lottie.github.io" rel="noopener noreferrer"&gt;lottie.github.io&lt;/a&gt;, driven by the Lottie Animation Community (LAC). That is the reference Lottie4J works against.&lt;/p&gt;

&lt;h2&gt;
  
  
  dotLottie: the format worth paying attention to
&lt;/h2&gt;

&lt;p&gt;Most Lottie work today happens with &lt;code&gt;.json&lt;/code&gt; files. The dotLottie format (&lt;code&gt;.lottie&lt;/code&gt;) is a ZIP container that can hold multiple animations, theming through slots, and interactive state machines, all in one file. It is where things are heading, and it is definitely what I need to add proper support for in Lottie4J.&lt;/p&gt;

&lt;p&gt;The difference matters practically. A &lt;code&gt;.lottie&lt;/code&gt; file compresses much better than the equivalent JSON, it can bundle assets cleanly, and the slots and states features open up real interactivity that a static JSON animation just cannot do.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for Lottie4J
&lt;/h2&gt;

&lt;p&gt;The conversation pushed me to think concretely about where to take the project next. Three areas stood out as important steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compliance checking against the LAC specification. Naail mentioned this is something the community wants more of. Knowing which features Lottie4J supports accurately gives the community a clear picture of where the project stands.&lt;/li&gt;
&lt;li&gt;Better dotLottie support, including multiple animations in a single file and eventually slots and state machines.&lt;/li&gt;
&lt;li&gt;The headless JavaFX 26 testing setup I recently documented is a real step forward for CI reliability. That blog post is &lt;a href="https://webtechie.be/post/2026-04-20-lottie4j-unit-test-with-headless-javafx/" rel="noopener noreferrer"&gt;here&lt;/a&gt; if you want the details.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another important moment in the conversation: Naail noted that interest in Lottie for desktop applications is growing, including from projects like KDE. JavaFX is in good company there! Even more right now, considering the recent announcement by Oracle that are now offering extended JavaFX support. This is something &lt;a href="https://www.azul.com/blog/the-javafx-revival-good-news-for-the-community-business-as-usual-for-azul/" rel="noopener noreferrer"&gt;Azul has done for years&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links from the video
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Lottie_(file_format)" rel="noopener noreferrer"&gt;Lottie format Wikipedia article&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Lottie4J

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://lottie4j.com" rel="noopener noreferrer"&gt;Lottie4J&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/lottie4j/" rel="noopener noreferrer"&gt;Sources on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webtechie.be/post/2026-04-20-lottie4j-unit-test-with-headless-javafx/" rel="noopener noreferrer"&gt;Blog: Headless unit testing with JavaFX 26&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Lottie

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://lottie.github.io/" rel="noopener noreferrer"&gt;Open-source specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/lottie/lottie.github.io/issues" rel="noopener noreferrer"&gt;Animation Community issues and spec work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lottie.github.io/implementations/" rel="noopener noreferrer"&gt;Community implementations list&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;LottieFiles:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://lottiefiles.com/free-animations/loading-bar" rel="noopener noreferrer"&gt;Free animations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lottiefiles.com/education" rel="noopener noreferrer"&gt;Education resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lottiefiles.com/integrations#embed-lottie-libraries" rel="noopener noreferrer"&gt;Integrations page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lottiefiles.github.io/lottie-docs/playground/json_editor/" rel="noopener noreferrer"&gt;Lottie JSON playground&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;LottieFiles Case Studies:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://lottiefiles.com/case-studies/Walmart" rel="noopener noreferrer"&gt;Walmart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lottiefiles.com/case-studies/cnn-create" rel="noopener noreferrer"&gt;CNN Create&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lottiefiles.com/case-studies/robinhood" rel="noopener noreferrer"&gt;Robinhood&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://apps.kde.org/glaxnimate/" rel="noopener noreferrer"&gt;Glaxnimate (KDE animation tool)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alexzhirkevich/compottie" rel="noopener noreferrer"&gt;Compottie (Kotlin Multiplatform player)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.thorvg.org/" rel="noopener noreferrer"&gt;ThorVG (C++ rendering library)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The conversation confirmed something I suspected: Lottie4J is not a niche experiment. Desktop animation is getting serious attention across the Java and Linux ecosystems. If you want to help shape where Lottie4J goes next, head over to &lt;a href="https://lottie4j.com" rel="noopener noreferrer"&gt;lottie4j.com&lt;/a&gt;. Pull requests, bug reports, and questions are all welcome in the comments of the video or in the &lt;a href="https://github.com/lottie4j/" rel="noopener noreferrer"&gt;Lottie4J GitHub project&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>javafx</category>
      <category>lottie</category>
      <category>desktop</category>
    </item>
    <item>
      <title>Debugging BentoFX in MelodyMatrix with Matt Coley, Scenic View, and an Honest Look at AI-Generated Code</title>
      <dc:creator>Frank Delporte</dc:creator>
      <pubDate>Tue, 21 Apr 2026 10:01:21 +0000</pubDate>
      <link>https://dev.to/fdelporte/debugging-bentofx-in-melodymatrix-with-matt-coley-scenic-view-and-an-honest-look-at-ai-generated-27c9</link>
      <guid>https://dev.to/fdelporte/debugging-bentofx-in-melodymatrix-with-matt-coley-scenic-view-and-an-honest-look-at-ai-generated-27c9</guid>
      <description>&lt;p&gt;There are bugs you can solve by yourself, and bugs where you just need to sit down with someone who knows the internals. This video is in the second category. &lt;a href="https://melodymatrix.rocks/" rel="noopener noreferrer"&gt;MelodyMatrix&lt;/a&gt; uses &lt;a href="https://github.com/Col-E/BentoFX" rel="noopener noreferrer"&gt;BentoFX&lt;/a&gt; for its dockable panel layout. Branches, leaves, tabs on the side, content panels that open and close. It works well until something fights the layout. But I had some visual problems I could not explain, some code that felt more complicated than it should be, and no good explanation for why.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/grwzIWWZMNw"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  When Your Layout Library Misbehaves, Call the Person Who Wrote It
&lt;/h1&gt;

&lt;p&gt;So I asked &lt;a href="https://www.coley.software/" rel="noopener noreferrer"&gt;Matt Coley&lt;/a&gt; if he had time to take a look. Matt is the creator of BentoFX, but he is also known for &lt;a href="https://www.jfx-central.com/showcases/recaf" rel="noopener noreferrer"&gt;Recaf&lt;/a&gt;, a bytecode editor for Java that itself uses BentoFX heavily as its UI framework. That means when Matt looks at a BentoFX integration. He wrote the library and uses it heavily in his own project. If you want the full background on his work, there is an earlier interview: &lt;a href="https://webtechie.be/post/2025-10-30-jfxinaction-matt-coley-recaf-bentofx-treemapfx-glcanvasfx/" rel="noopener noreferrer"&gt;JavaFX In Action #22 with Matt Coley&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What BentoFX Actually Does
&lt;/h2&gt;

&lt;p&gt;BentoFX is a docking and layout library for JavaFX. The core idea is that you have a tree of containers: branches split the space horizontally (or vertically if you rotate them), and leaves hold the actual content. Tabs let you stack multiple panels in one leaf.&lt;/p&gt;

&lt;p&gt;In MelodyMatrix, this maps to a sidebar on the left, a main content area in the middle, and additional panels on the right. It is a clean model when you understand it, but there are some behaviors that are not immediately obvious. Pruning is one of them: when you close a panel, BentoFX removes the empty container and reorganizes the remaining branches. That is usually what you want, but if you are also managing widths or visibility yourself in code, things start to conflict. That was part of my problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenic View: Browser DevTools for JavaFX
&lt;/h2&gt;

&lt;p&gt;This session reminded me that &lt;a href="https://www.jfx-central.com/tools/scenicview" rel="noopener noreferrer"&gt;Scenic View&lt;/a&gt; exists and I should have been using it much earlier.&lt;/p&gt;

&lt;p&gt;If you have done any web development, you know how useful browser inspect tools are. You hover over an element, you can see exactly what CSS is applied, what the padding is, why something is shifted by 12 pixels. Scenic View does the same thing for a running JavaFX application. It attaches to your running application and shows the scene graph live with all the layout properties.&lt;/p&gt;

&lt;p&gt;We pulled it during the video to look at the tab header rotation issue I was having. Instead of guessing which CSS rule was causing the offset, we could see exactly what was happening in the layout tree. Embarrassingly, I had not used it before this session. It should be in your JavaFX debugging toolkit!&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Honest Thoughts on AI-Generated Code
&lt;/h2&gt;

&lt;p&gt;We also spent some time looking at code that had been written or modified by Claude and Copilot. Matt spotted it quickly: the code had the patterns you recognize after reviewing AI-generated output. The rotation fix for the tab headers was one example. The code did technically work, but it was fragile: it used CSS class lookups that would break if BentoFX changed anything internally. Matt's take was pragmatic: if it works now, and you have a way to update it when it breaks, fine. But it is worth knowing that is what you are dealing with.&lt;/p&gt;

&lt;p&gt;The broader cleanup was straightforward once Matt explained how BentoFX manages its own leaf widths. I had added animation and width-tracking code essentially fighting the library. Removing it simplified things considerably and fixed one of the issues in the process. Not all AI-generated code is bad. But a code review from someone who knows the library beats prompting your way through it. This session was a good reminder of that.&lt;/p&gt;

&lt;h2&gt;
  
  
  We Might Have Found a Bug
&lt;/h2&gt;

&lt;p&gt;BentoFX did something unexpected with divider modes when re-opening a panel that had been closed. Matt looked at his own source and said he suspected something was not right there. It is a 0.x library, but it is actively maintained and used in production in Recaf. Matt's response was immediate: file a ticket with reproduction steps.&lt;/p&gt;

&lt;h1&gt;
  
  
  Timeline
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;00:00 Introduction: Matt Coley, Recaf, BentoFX&lt;/li&gt;
&lt;li&gt;02:19 How BentoFX is used in MelodyMatrix&lt;/li&gt;
&lt;li&gt;04:18 Visual problems with the BentoFX integration&lt;/li&gt;
&lt;li&gt;06:33 A look into the MelodyMatrix code&lt;/li&gt;
&lt;li&gt;09:15 Changing tab position from top to side, and why the project uses Kotlin&lt;/li&gt;
&lt;li&gt;16:25 Using Scenic View to debug a JavaFX layout live&lt;/li&gt;
&lt;li&gt;22:38 How pruning affects visual BentoFX components&lt;/li&gt;
&lt;li&gt;34:53 Cleaning up unneeded code: let BentoFX handle leaf widths, removing animations&lt;/li&gt;
&lt;li&gt;41:33 We probably found a bug in BentoFX, and a look into the BentoFX source&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Links
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webtechie.be/post/2025-10-30-jfxinaction-matt-coley-recaf-bentofx-treemapfx-glcanvasfx/" rel="noopener noreferrer"&gt;JavaFX In Action #22 with Matt Coley, diving into bytecode and JARs with Recaf and JavaFX libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Matt Coley: 

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Col-E" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.coley.software/" rel="noopener noreferrer"&gt;Website&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jfx-central.com/people/m.coley" rel="noopener noreferrer"&gt;JFX Central&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;JavaFX libraries by Matt: 

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Col-E/BentoFX" rel="noopener noreferrer"&gt;BentoFX&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Col-E/TreeMapFX" rel="noopener noreferrer"&gt;TreeMapFX&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Col-E/GLCanvasFX" rel="noopener noreferrer"&gt;GLCanvasFX&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;On JFX Central: 

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.jfx-central.com/libraries/bentofx" rel="noopener noreferrer"&gt;BentoFX&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.jfx-central.com/showcases/recaf" rel="noopener noreferrer"&gt;Recaf&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.jfx-central.com/libraries/pdffx" rel="noopener noreferrer"&gt;PDFViewFX&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jfx-central.com/tools/scenicview" rel="noopener noreferrer"&gt;Scenic View&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;MelodyMatrix: 

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://melodymatrix.rocks/" rel="noopener noreferrer"&gt;Website&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/codewriterbv/melodymatrix-app-views" rel="noopener noreferrer"&gt;Open-source viewers on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>javafx</category>
      <category>bentofx</category>
      <category>melodymatrix</category>
    </item>
  </channel>
</rss>
