<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged divooka</title>
<link>https://coderlegion.com/tag/divooka</link>
<description>Powered by Question2Answer</description>
<item>
<title>DevLog 20261002: Desktop Automation Toolbox and Mouse Emulation</title>
<link>https://coderlegion.com/29552/devlog-20261002-desktop-automation-toolbox-and-mouse-emulation</link>
<description>&lt;h3 id=&quot;devlog-20261002-desktop-automation-toolbox-and-mouse-emulation&quot;&gt;DevLog 20261002: Desktop Automation Toolbox and Mouse Emulation&lt;/h3&gt;
&lt;p&gt;I remember when I used to use Macro Recorder to automate simple repetitive tasks — I especially liked its macro recording feature. It requires no programming and can easily get tedious jobs involving clicking and waiting done.&lt;/p&gt;
&lt;p&gt;Since then, I've long moved past automating with basic mouse/keyboard emulation and have sunk deep into an API-driven approach — I just love how clean everything is when you can really program it. Well, that's a major reason why I started Divooka in the first place — lots of ecosystems and lots of tools we use are broken beyond repair and inherently NOT designed to be automated cleanly. &lt;a rel=&quot;nofollow&quot; href=&quot;https://medium.methodox.io/opinion-some-reflections-on-mcp-4d4d24d76779&quot;&gt;MCP&lt;/a&gt; partly addressed that problem, but it's really just a modern remedy for mediocre design.&lt;/p&gt;
&lt;p&gt;Either way, when I set out to implement procedural context for Divooka, desktop automation is nonetheless one of the first things that comes to mind. As we get more mature in &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20260319-towards-oop-document-level-main-module-and-module-level-members-and-behaviors-133b&quot;&gt;OOP programming&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250710-generics-in-divooka-49e6&quot;&gt;generics&lt;/a&gt;, and recently achieved a true &lt;a rel=&quot;nofollow&quot; href=&quot;https://lnkd.in/p/d2UyzBVw&quot;&gt;data structure algorithm implementation&lt;/a&gt; in Divooka, that is drawing closer.&lt;/p&gt;
&lt;p&gt;In this &lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/x8_Qjephn4o&quot;&gt;short video&lt;/a&gt;, I show a desktop example for the automation toolbox that will come with the upcoming &lt;a rel=&quot;nofollow&quot; href=&quot;https://store.steampowered.com/app/4422920/Divooka_Explore/&quot;&gt;Divooka Explore&lt;/a&gt;. It's small, but it shows quite a few interesting things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It creates a countdown GUI using the Immediate GUI framework.&lt;/li&gt;
&lt;li&gt;It interacts with an opened window — in this case, the Paint program — through keyboard shortcuts and mouse input.&lt;/li&gt;
&lt;li&gt;It can parse a stroke-based font at runtime and draw text with it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you ask me, that's a lot of cool features in such a small example! Really excited, and there's still more work to do, so see you soon!&lt;/p&gt;
</description>
<category>Videos</category>
<guid isPermaLink="true">https://coderlegion.com/29552/devlog-20261002-desktop-automation-toolbox-and-mouse-emulation</guid>
<pubDate>Fri, 02 Oct 2026 15:16:32 +0000</pubDate>
</item>
<item>
<title>Two-Year Reflection on Methodox and the Development of Divooka</title>
<link>https://coderlegion.com/27868/two-year-reflection-on-methodox-and-the-development-of-divooka</link>
<description>&lt;blockquote&gt;&lt;p&gt;Still going strong, and hopefully we will be ready soon.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;the-divooka-story&quot;&gt;The Divooka Story&lt;/h3&gt;
&lt;p&gt;It's surprising to count—it has already been so many years since the first prototype in 2022, the later attempted updates in 2023/2024, the first &quot;beta&quot; in September 2024 that we later downplayed to something closer to an &quot;alpha&quot;, the internal builds throughout 2025, the much greater feature expansion towards 2026, and now, over the past few months, what I hope will be the &quot;final&quot; major round of architectural refactoring around the runtime, file format, package management, and features needed for professional use. I just wish I didn't age at the same rate as my software concept.&lt;/p&gt;
&lt;p&gt;I spent a lot of time earlier this year, particularly around June, polishing DSLs—Domain-Specific Libraries—because without a useful set of higher-level capabilities, Divooka is JUST a language. A language can be technically interesting while still leaving the user to build everything themselves; the point of Divooka is eventually to provide useful computational building blocks at a much higher level. The areas I've chosen to focus on are naturally the areas I know reasonably well—not necessarily the areas where I would claim to be the world's greatest expert, but areas where I think we can make genuinely useful tools for everyday work: data processing, procedural terrain generation, graphics and media work, automation, and office productivity.&lt;/p&gt;
&lt;p&gt;The procedural context in particular took a lot of effort to conceptualize. I wanted to address several problems I have experienced with node-based systems such as Unreal Engine's Blueprint: large procedural graphs can become difficult to restructure, reuse and reason about, while certain programming patterns are awkward to express visually in the first place. Divooka's procedural context is my attempt to retain the visibility of graph-based programming without pretending that traditional programming concepts such as loops, branches, functions and structured control flow somehow stopped being useful. The move towards a compiled runtime was another major jump, and really only came together around April and May 2026. Our Mandelbrot benchmark makes the motivation rather obvious: compilation changed the performance characteristics dramatically compared with execution through the older editor/runtime paths. Fully embracing compilation has been a major undertaking, but I increasingly see it as essential if Divooka is going to combine very high-level programming with acceptable performance. More details and the limitations of the benchmark are published in the GitHub repository linked below.&lt;/p&gt;
&lt;p&gt;The file format, runtime and standardization work has been another recurring problem. This is also why we have deliberately slowed down on declaring a final version of DiDS or other Divooka Open Standards. The formats have changed A LOT as the architecture has matured, and publishing something as &quot;final&quot; before we are prepared to support it for years would create more problems than it solves. I don't see much value in pretending the specification is frozen merely for the satisfaction of calling it 1.0. Stability matters, interoperability matters, and eventually backward compatibility matters—but those things are much easier to promise after the underlying system has actually settled.&lt;/p&gt;
&lt;p&gt;So where are we going? General-purpose at the core, high utility and high performance, while keeping a super-high-level interface closer to the experience of scripting languages than traditional application development. Nodes where nodes make things clearer, code where code makes things clearer, and enough interoperability underneath that users are not trapped inside an isolated visual-programming island. For people already familiar with programming language design, scripting systems and visual development environments, that probably explains most of the idea. At this point, I am content with that.&lt;/p&gt;
&lt;h3 id=&quot;internal-and-financial&quot;&gt;Internal and Financial&lt;/h3&gt;
&lt;p&gt;The financial side became more challenging as we moved through 2026. We are now entering our third year and completing our second, and naturally the resources that felt relatively abundant when I started the company in 2024 are more constrained today. Legal work, IP, infrastructure, hiring and contracting all cost real money, and founder time has a cost even when it doesn't appear as a salary on an income statement. I also did not exactly enter this adventure after ten years of living like a monk and accumulating an infinite personal runway. At some point every bootstrapped founder has to ask the uncomfortable but necessary question: how long can this continue before the business itself has to carry more of the weight?&lt;/p&gt;
&lt;p&gt;That doesn't mean I suddenly regret doing it. I am still relatively young—a little over 30—but two years is long enough that the startup can no longer be treated as a temporary experiment detached from the rest of life. I have to think about long-term financial stability, savings, retirement and everything else that continues existing regardless of whether somebody decides to build a visual programming system. There is no magical funding event I can responsibly assume will arrive and solve those questions for me, so one way or another the company has to become increasingly sustainable on its own. In that sense, commercialization is becoming more important, not because the R&amp;amp;D suddenly matters less, but because the only durable way to continue doing the R&amp;amp;D is to build a business capable of supporting it.&lt;/p&gt;
&lt;p&gt;I doubt I would particularly enjoy going back to conventional corporate life, although occasionally I do miss parts of it: working in those skyscrapers, looking down at people and cars twenty floors below as little moving dots, the view of Lake Ontario, interesting corporate events, colleagues with very different expertise, and simply being part of something much larger. But it is fundamentally different from being one's own boss. In a large organization I could sometimes lose sight of why a particular piece of work existed in the first place; here, almost every decision is connected directly to something we eventually put in front of users. Maybe one day a smaller studio environment would suit me too, but there is something uniquely rewarding about being able to make a decision, build the thing, and then see something made by us actually published.&lt;/p&gt;
&lt;p&gt;That is something to think about. We need to act soon—not out of panic, but because after two years, sustainability deserves to be treated as part of the engineering problem rather than something outside of it.&lt;/p&gt;
&lt;h3 id=&quot;noise-splashes-and-small-gadgets&quot;&gt;Noise, Splashes and Small Gadgets&lt;/h3&gt;
&lt;p&gt;The development process also spun off a surprising number of smaller products. Some were experiments, some became quite handy, and some have already gone through several iterations of their own. I don't really see these as completely separate from Divooka: in many cases they came from technologies, libraries, ideas or workflows developed while working on the larger system, and they give us smaller places to test whether those pieces are actually useful outside the environment in which they were invented.&lt;/p&gt;
&lt;p&gt;Released / scheduled: Fantasy Planet Painter, A Detective Story and Divooka Explore.&lt;/p&gt;
&lt;p&gt;Smaller apps: Methodox Threads, Methodox Voxel Sandbox, and Transcriber—which has been surprisingly popular as a free tool.&lt;/p&gt;
&lt;p&gt;It sometimes feels like we spent a lot of time on each of these, and then when I actually count the list it somehow doesn't look like that much. Such is software development.&lt;/p&gt;
&lt;h3 id=&quot;public-facing&quot;&gt;Public-Facing&lt;/h3&gt;
&lt;p&gt;On the product-readiness side, we've updated the official Methodox website, added dedicated websites for the main products, greatly expanded the Methodox Wiki, and gone through migrations away from systems such as Wiki.js and WordPress in the process. The public surface of a software company is itself a substantial product: documentation, downloads, storefronts, licensing information, release notes, product pages and examples all have to remain reasonably synchronized with software that is changing underneath them. That work is less glamorous than implementing a compiler or graph runtime, but without it there isn't really a product that an outside person can confidently understand or use.&lt;/p&gt;
&lt;p&gt;The CI, packaging and deployment side has improved as well. Those processes had remained painfully manual for much longer than I would have liked, and ironing out the development pipeline has been one of those improvements that is difficult to advertise but immediately makes everything else easier. Releasing software should eventually become boring. If every build and publication feels like a special event requiring a checklist of mysterious manual steps, something in the engineering process is still unfinished.&lt;/p&gt;
&lt;h3 id=&quot;lessons&quot;&gt;Lessons&lt;/h3&gt;
&lt;p&gt;Looking back to my reflection from July 8 last year, the burden from training, external obligations and general management has been reduced considerably. A surprising amount of time last year disappeared into accounting and tax work, which was not exactly why I founded a software company, but I suppose—and I must believe—that doing something three times makes the fourth much less intimidating. More importantly, I think I now have a better understanding of what actually makes a &quot;product&quot;, especially the amount of work between something technically functioning and something polished enough to release, document, announce and support. A feature being implemented is not the same thing as a product being finished. That distinction sounds obvious until you repeatedly experience how much work lives between those two points.&lt;/p&gt;
&lt;p&gt;One thing I still don't do enough is &quot;engage with the community&quot;. I understand why people keep emphasizing it, and I don't think building completely in isolation is healthy, but my view has become more nuanced over the past year: community engagement becomes substantially more useful when there is something coherent for that community to engage with. Feedback on a moving architectural target can easily turn into noise for both sides. Divooka Explore's Early Access period should give us a better mechanism for that interaction, because users can increasingly react to a functioning system rather than an abstract promise.&lt;/p&gt;
&lt;p&gt;Things such as cloud services through Methodox One, AI integration through projects such as Chat Gateway, and various other interesting ideas people suggested all pulled my attention in different directions. In retrospect, the core runtime was simply not ready enough for that level of expansion, even towards the middle of 2026. It was really only over the past month or so that I began feeling that the foundation was taking good shape and, more importantly, starting to feel robust and trustworthy. The runtime took an unexpectedly long time to develop even though I am no stranger to making scripting systems: EasyShell worked well for what it was designed to do, and I have been using Pure in one form or another since my time at OTPP. But Divooka is a fundamentally larger undertaking. Creating a new syntax, graph model, execution system, editor, IDE, packages, interoperability layer and application model from the ground up is very different from building a convenient language layer on top of an existing interface. We absolutely use existing foundations—Roslyn, .NET, C#, Python and their enormous ecosystems—but there are still a remarkable number of edges that become our responsibility the moment we try to make them feel like one coherent system.&lt;/p&gt;
&lt;p&gt;Launching the product has always been a priority, but during roughly the first year and a half I have to admit that I spent too much attention on what I thought I was supposed to be doing as an &quot;entrepreneur&quot;: conferences, investor calls, chasing VC conversations, networking because people said networking was important, and generally talking about building the company while I was still trying to build the thing the company exists to make. Some friends still tell me I haven't talked to enough people, and they may well have a point. But I've also learned that there is a limit to how much external motion is useful before it becomes noise. Right now I care much more about making Divooka real, solid and usable. There will always be more people to talk to afterwards.&lt;/p&gt;
&lt;h3 id=&quot;the-ai-gain&quot;&gt;The AI Gain&lt;/h3&gt;
&lt;p&gt;I am finally starting to officially &quot;use AI&quot; in a much more meaningful way—more precisely, coding agents. I have used ChatGPT on a near-daily basis for quite a long time, while also being fairly critical of media coverage portraying every new model as immediately revolutionary. For a long time I simply didn't find the reality matched the hype. That has changed considerably through the middle of 2026, and especially since late July. Coding agents have become useful enough that I can no longer dismiss them as an unreliable novelty: for certain kinds of implementation, research, repetitive work and review, they are genuinely efficient.&lt;/p&gt;
&lt;p&gt;That does not mean I suddenly believe an AI is going to independently build Divooka for us. In practice it feels much more like a reduction in the cost of certain kinds of labour. It can generate, inspect, compare, research and iterate very quickly, but somebody still has to decide what ought to exist, whether the proposed architecture makes sense, whether the output actually works, and whether any of it belongs in the product. If anything, human capacity has become a more obvious bottleneck: judgment, prioritization and verification do not disappear merely because implementation becomes cheaper. The useful question is no longer &quot;is AI useful?&quot; but &quot;which work should we delegate to it, which work should we keep doing ourselves, and how do we verify the difference?&quot; That is a much more practical question, and one I expect will continue changing rapidly.&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;The one thing I would most like to see now is Divooka properly released. But my philosophy throughout this project has been that some steps simply cannot be skipped. That is part of the reason I quit my job and went deep into R&amp;amp;D in the first place: if the only objective had been to ship something quickly, there were much easier products I could have chosen to build.&lt;/p&gt;
&lt;p&gt;I still look forward to the day when I can produce a fully illustrated, properly written &lt;em&gt;Introduction to Visual Programming with Divooka&lt;/em&gt;. In my head I can already see it as an entire textbook covering a way of thinking about programs that is still poorly represented in conventional computer science education—not &quot;visual programming&quot; as a collection of toy blocks, and not simply a diagrammatic wrapper around scripting, but a serious treatment of graphs as a practical programming medium alongside text. If we can eventually build a system rich enough that such a book genuinely deserves to exist, I think that will be an achievement in its own right.&lt;/p&gt;
&lt;p&gt;And I think that is worth taking the time to get right.&lt;/p&gt;
&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Methodox Official Website: &lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox.io&quot;&gt;https://methodox.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Divooka Official Website: &lt;a rel=&quot;nofollow&quot; href=&quot;https://divooka.com&quot;&gt;https://divooka.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Methodox Wiki: &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io&quot;&gt;https://wiki.methodox.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Methodox Apps: &lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox.app&quot;&gt;https://methodox.app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Steam Divooka Explore page: &lt;a rel=&quot;nofollow&quot; href=&quot;https://store.steampowered.com/app/4422920/Divooka_Explore/&quot;&gt;https://store.steampowered.com/app/4422920/Divooka_Explore/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Steam Fantasy Planet Painter page: &lt;a rel=&quot;nofollow&quot; href=&quot;https://store.steampowered.com/app/3336490/Fantasy_Planet_Painter/&quot;&gt;https://store.steampowered.com/app/3336490/Fantasy_Planet_Painter/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Itch.io Store Front: &lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox.itch.io&quot;&gt;https://methodox.itch.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Mandelbrot Benchmark: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MethodoxTech/DivookaBenchmark_Mandelbrot&quot;&gt;https://github.com/MethodoxTech/DivookaBenchmark_Mandelbrot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27868/two-year-reflection-on-methodox-and-the-development-of-divooka</guid>
<pubDate>Sun, 20 Sep 2026 22:00:11 +0000</pubDate>
</item>
<item>
<title>Methodox Voxel Sandbox | Launch Trailer | Methodox Voxel Scene Viewer</title>
<link>https://coderlegion.com/25458/methodox-voxel-sandbox-launch-trailer-methodox-voxel-scene-viewer</link>
<description>&lt;p&gt;&lt;strong&gt;Methodox Voxel Sandbox&lt;/strong&gt; is a Windows-only voxel world generator, editor and renderer. You can randomly generate a voxel environment, build inside it, then photograph it with a digital camera.&lt;/p&gt;
&lt;h3 id=&quot;core-features&quot;&gt;Core Features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Seed-Based World Generation&lt;/strong&gt;: Automatically rolls and generates expansive voxel environments from a given seed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First-Person Editor&lt;/strong&gt;: Walk through the world from a first-person perspective to interact, break blocks, and build new structures.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Progressive Path Tracer (Photo Mode)&lt;/strong&gt;: Renders scenes progressively with light bouncing, glass refraction, metal reflections, glowing objects, clouds, and soft shadows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advanced Camera and Image Controls&lt;/strong&gt;: Fine-tune renders with settings for lenses, lights, quality, exposure, and tone mapping.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;255-Slot Material Palette&lt;/strong&gt;: Build with terrain, vegetation, ores, glass, metal, lights, and full color ramps.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Methodox Voxel Scene Viewer&lt;/strong&gt;: Included download tool that opens external voxel scenes (such as .vox, Goxel, OpenVDB, and MCEdit schematics) at any scale to render them with the path-tracing engine.&lt;/p&gt;
</description>
<category>Videos</category>
<guid isPermaLink="true">https://coderlegion.com/25458/methodox-voxel-sandbox-launch-trailer-methodox-voxel-scene-viewer</guid>
<pubDate>Wed, 26 Aug 2026 18:55:20 +0000</pubDate>
</item>
<item>
<title>DevLog 20260731: FPP v1.4.0 and Divooka Explore Demo Updates</title>
<link>https://coderlegion.com/23943/devlog-20260731-fpp-v1-4-0-and-divooka-explore-demo-updates</link>
<description>&lt;h3 id=&quot;overview&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;The past few months have seen several &lt;a rel=&quot;nofollow&quot; href=&quot;https://store.steampowered.com/app/4770820/Divooka_Explore_Demo/&quot;&gt;Divooka Explore Demo&lt;/a&gt; releases, with our efforts focused on rolling out procedural terrain generation features. These include random noise-based terrain generation, procedural and simulation-based terrain processing, natural effects such as erosion and hydrology analysis, procedural coloration and texturing, and, more recently, improved 3D previews of generated terrains directly within the Divooka visual programming environment.&lt;/p&gt;
&lt;p&gt;One small lesson from this cumulative effort is that there always seems to be more academic material, papers, tutorials, online examples, and even source code snippets to discover and use to improve existing systems. That is a shiny silver lining, since we are still fairly new to this domain and desperately need broad exposure to existing techniques.&lt;/p&gt;
&lt;p&gt;Another useful lesson is that, although many papers approach these subjects from a real-time rendering perspective - such as the old GPU Gems articles - the same techniques are often equally applicable in a pure-CPU regime.&lt;/p&gt;
&lt;h3 id=&quot;procedural-terrain-generation-in-divooka&quot;&gt;Procedural Terrain Generation in Divooka&lt;/h3&gt;
&lt;p&gt;As mentioned, we have made quite a large number of functions available in this area, and the subject definitely warrants much more detailed treatment. Documentation and comprehensive video guides are dearly needed.&lt;/p&gt;
&lt;p&gt;To recap, we have moved from very basic noise-based terrain generation to custom heterogeneous multifractal terrains, functional erosion - although there is still plenty of room for improvement - various masking techniques, practical procedural coloring using either masks or SQL queries, and preliminary texturing support.&lt;/p&gt;
&lt;p&gt;The terrain generation is only the first step, and there is still plenty of room for improvement despite we have achieved useful basic implementation for now.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/tjh15wb15uew55mjoroo.png&quot; alt=&quot;Early Days of Divooka Procedural Terrain Generation&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/5yejmj3d6u1ltjp3tuvn.png&quot; alt=&quot;Early Days of Heightmap Erosion in Divooka&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once we became comfortable with generating functional procedural terrains, we could move on to coloring, texturing, and eventually shading.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/pgfyqwl0v6t7s50ckxya.png&quot; alt=&quot;Procedural Terrain Coloring Nodes in Divooka&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/mtpjb24wj0jmpqwikmme.png&quot; alt=&quot;Procedural Terrain Coloring in Divooka&quot;&gt;&lt;/p&gt;
&lt;p&gt;At this point, I realized a fundamental limitation of heightmaps and image textures. They may be able to represent distant views of terrains spanning 8–64 km when viewed from above, but for ground-level rendering, procedural shading techniques are a must.&lt;/p&gt;
&lt;p&gt;The 3D rendering work is still new, and there is a long way to go. We started with basic baked shadows, moved on to experimental texture mixing, and eventually reached full PBR rendering.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/vd23at51fda31xhie5rc.png&quot; alt=&quot;Baked Shadow Mapping for Terrains in Divooka&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/eyzib5rfsvljdhkmqslh.png&quot; alt=&quot;Early days of Procedural Texturing of Terrains with Photo and Generated Textures&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/89jvthtqlgj6nzcm6x0x.png&quot; alt=&quot;Voxel Space Viewer of Textured Terrains in Divooka&quot;&gt;&lt;/p&gt;
&lt;p&gt;PBR rendering is the holy grail. Once we figure out how to dynamically populate terrains with trees, grass, and rocks - and generate ripples across reflective water - we will be able to share all of it through Divooka as reproducible workflows!&lt;/p&gt;
&lt;p&gt;Below is an intermediate result so far. It uses simple slope-based texturing, which we plan to replace with mask-layer-based materials in the next attempt.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/6xv7t457d8ij3tvqozva.png&quot; alt=&quot;Procedural Slope-Based Textures for Procedural Terrains in Divooka, PBR Rendering&quot;&gt;&lt;/p&gt;
&lt;p&gt;We have learned so much and cannot wait to share more about the specific techniques once we have more spare time!&lt;/p&gt;
&lt;h3 id=&quot;the-fantasy-planet-painter-v1-4-0-update&quot;&gt;The Fantasy Planet Painter v1.4.0 Update&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/oqdj4ehduk7s3pcqyc3w.png&quot; alt=&quot;2D Flat World in FPP - Basic Altitude Coloring to Relief Render&quot;&gt;&lt;/p&gt;
&lt;p&gt;While the eventual goal is to support whole-planet procedural generation and content management, we began with basic Earth-like world map generation. At the moment, our work is mostly focused on improving the existing editing experience. The previous update significantly improved the multithreaded generation performance of the tetrahedral subdivision method.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/s8zbkyenxvetcp0aln6r.png&quot; alt=&quot;Fantasy Planet Painter by Methodox&quot;&gt;&lt;/p&gt;
&lt;p&gt;The 2D cell-based height-value canvas is an interesting medium. It allows us to sketch the desired elevation levels quickly at a high level while the program procedurally populates the remaining details.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/3d6pw0r6vwttkunvlyjz.png&quot; alt=&quot;Painting on a 2D Cell Map with Abstract Height Values While FPP Generate Procedural Details&quot;&gt;&lt;/p&gt;
&lt;p&gt;There has also been some experimental exploration into using this medium for direct 2D terrain editing, as seen in our previous video, &lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/JDSvBHWeLQg?si=MEAZdVL4sYn674m-&quot;&gt;World to Beyond (BAR map export from FPP)&lt;/a&gt;. Although BAR export is still in its early stages, the potential of this kind of authoring technique is indeed interesting.&lt;/p&gt;
&lt;p&gt;Following the advances in procedural terrain generation within Divooka, we have gathered quite a collection of useful tools and made them available at a high level in the latest &lt;a rel=&quot;nofollow&quot; href=&quot;https://store.steampowered.com/news/app/3336490/view/697645818486718921&quot;&gt;FPP update&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/hb74s7fqo75qsemf5tuu.png&quot; alt=&quot;Hydrology (Erosion) Outputs from Divooka&quot;&gt;&lt;/p&gt;
&lt;p&gt;The results are very interesting, and I am glad that we are moving beyond practical utility toward generating worlds that - at least in my eyes - are also visually appealing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/eybl1vnbexwd2hrv99s5.png&quot; alt=&quot;2D Flat World in FPP - 1K Wetness + Slope-Based Relief Render&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;That is a brief recap for today. I have been busy diving headfirst into all the nitty-gritty work, which has left less time for DevLog updates and for sharing the techniques and progress behind them.&lt;/p&gt;
&lt;p&gt;Still, let us look forward to the day when new results are ready to shine, when we are comfortable placing an early-access version of Divooka into the hands of users, and when we can talk in much greater detail about the road that led us there!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23943/devlog-20260731-fpp-v1-4-0-and-divooka-explore-demo-updates</guid>
<pubDate>Sat, 01 Aug 2026 18:44:44 +0000</pubDate>
</item>
<item>
<title>Designing a Binary Data API for Divooka with PPM and PLY Examples</title>
<link>https://coderlegion.com/17640/designing-a-binary-data-api-for-divooka-with-ppm-and-ply-examples</link>
<description>&lt;p&gt;Revision: 001&lt;br&gt;
Last Update: 2026-05-12&lt;/p&gt;
&lt;p&gt;One thing I feel is not discussed enough in typical programming conversations is working with binary data. It often seems as though there is a ready-to-use library for whatever data type we need. But that illusion breaks whenever we want to do something new, interface with a less common format, work with legacy data, or build high-performance applications around custom data formats.&lt;/p&gt;
&lt;p&gt;Designing a custom data format that fits the application at hand is an important skill. In this post, however, we will focus less on the philosophy and more on the operational details.&lt;/p&gt;
&lt;p&gt;Binary formats are everywhere: images, meshes, save files, network packets, custom asset bundles, compressed streams, hardware protocols, game data, and many old file formats that are still useful decades later. Yet in application code, binary parsing often becomes messy very quickly. You start with a &lt;code&gt;byte[]&lt;/code&gt;, then an offset integer, then a few helper methods, then nested offset calculations. Before long, the parser has two responsibilities at once: understanding the file format and manually managing where the cursor is.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ptedroqt0rgdz8iz5u7.png&quot; alt=&quot;File Container Formats&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;File Container Formats &lt;a rel=&quot;nofollow&quot; href=&quot;https://upload.wikimedia.org/wikipedia/commons/a/ab/Container_Formats_Examples.svg&quot;&gt;WikiCommons&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;design-motivation&quot;&gt;Design Motivation&lt;/h3&gt;
&lt;p&gt;At a higher abstraction level, the goal of Divooka is to move cursor-management logic into something closer to &lt;a rel=&quot;nofollow&quot; href=&quot;https://kaitai.io/&quot;&gt;Kaitai Struct&lt;/a&gt; or DFDL, the Data Format Description Language. This makes it easier to work with standardized, well-documented formats such as &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Resource_Interchange_File_Format&quot;&gt;RIFF&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Instead of writing code like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;int offset = 0;

string magic = Encoding.ASCII.GetString(bytes, offset, 4);
offset += 4;

int version = BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(offset, 4));
offset += 4;

int count = BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(offset, 4));
offset += 4;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we want this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;var remaining = ImmutableByteArrayReader.FromFile(&quot;Data.bin&quot;)
    .ReadString(4, out string magic)
    .ReadInt(out int version)
    .ReadInt(out int count);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is similar in spirit to a stream-based API:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;using var reader = new BinaryReader(stream, Encoding.ASCII, leaveOpen: true);

string magic = Encoding.ASCII.GetString(reader.ReadBytes(4));

int version = reader.ReadInt32();
int count = reader.ReadInt32();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The involved types here are &lt;code&gt;ImmutableByteArrayReader&lt;/code&gt;, &lt;code&gt;MutableByteArrayWriter&lt;/code&gt;, and &lt;code&gt;ImmutableByteArrayWriter&lt;/code&gt;, all exposed through a fluent API. The parser reads like the binary format itself.&lt;/p&gt;
&lt;p&gt;The writer mirrors that idea:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;var data = new MutableByteArrayWriter()
    .WriteStringFixedSize(&quot;DATA&quot;)
    .WriteInt(version)
    .WriteInt(count);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The idea is simple: binary data should be represented as a sequence of explicit read and write operations, not as scattered offset arithmetic.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hcq83jbk0t3csy7qh6lt.png&quot; alt=&quot;Binary Data Reading and Writing in Divooka&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;reading-binary-data&quot;&gt;Reading Binary Data&lt;/h3&gt;
&lt;h4 id=&quot;binary-buffer-reader-immutablebytearrayreader&quot;&gt;Binary Buffer Reader: &lt;code&gt;ImmutableByteArrayReader&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;ImmutableByteArrayReader&lt;/code&gt; is a reader-oriented abstraction over binary data.&lt;/p&gt;
&lt;p&gt;It is immutable. Reading from it does not mutate the current object. Instead, read operations return the remaining unread portion of the buffer.&lt;/p&gt;
&lt;p&gt;The main API shape is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public ImmutableByteArrayReader ReadInt(out int value);
public int ReadInt();
public int ReadIntWithOffset(int byteIndex);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These three forms cover three common use cases.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;out&lt;/code&gt; form is for fluent parsing:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;var remaining = data
    .ReadString(4, out string magic)
    .ReadInt(out int width)
    .ReadInt(out int height);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The direct-return form is for reading a value from the start of a buffer when the remaining data is not needed:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;int width = data.ReadInt();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The offset form is for random access without consuming anything:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;int width = data.ReadIntWithOffset(4);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives the type this clear behavior: Sequential reads consume by returning a new remaining buffer. Offset reads do not consume. Because the reader is immutable, slicing can be cheap: reading a subrange can return another reader over the same underlying data instead of copying bytes.&lt;/p&gt;
&lt;p&gt;That means this kind of operation is cheap:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;var remaining = data.ReadBytes(32, out ImmutableByteArrayReader header);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both &lt;code&gt;remaining&lt;/code&gt; and &lt;code&gt;header&lt;/code&gt; can be views over the same internal byte array. Since neither object can mutate the backing storage, this remains safe.&lt;/p&gt;
&lt;h4 id=&quot;reading-blocks&quot;&gt;Reading blocks&lt;/h4&gt;
&lt;p&gt;Binary formats often have fixed-size blocks. For example, a file might contain a 32-byte header, followed by 32 bytes of padding, followed by records.&lt;/p&gt;
&lt;p&gt;The API shape for bytes is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public ImmutableByteArrayReader ReadBytes(int count, out ImmutableByteArrayReader value);
public ImmutableByteArrayReader ReadBytes(int count);
public ImmutableByteArrayReader ReadBytesWithOffset(int byteIndex, int count);
public ImmutableByteArrayReader ReadBytes(int count, out ImmutableByteArrayReader value, Action&amp;lt;ImmutableByteArrayReader&amp;gt; continuation);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The continuation form is useful when a sub-block should be parsed separately:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;int versionNumber = 0;

var remaining = ImmutableByteArrayReader.FromFile(&quot;MyData.bin&quot;)
    .ReadString(4, out string magic)
    .ReadBytes(32, out ImmutableByteArrayReader _, header =&amp;gt; header.ReadUInt(out versionNumber))
    .ReadBytes(32, out _)
    .ReadInt(out int count);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main stream advances by 32 bytes, while the continuation reads from the isolated header block.&lt;/p&gt;
&lt;p&gt;This is especially useful for formats where blocks have fixed sizes, but only some fields inside the block are currently relevant.&lt;/p&gt;
&lt;h4 id=&quot;strings&quot;&gt;Strings&lt;/h4&gt;
&lt;p&gt;Strings usually appear in binary formats in two common forms.&lt;/p&gt;
&lt;p&gt;The first is fixed-size:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public ImmutableByteArrayReader ReadString(int size, out string value);
public string ReadString(int size);
public string ReadStringWithOffset(int byteIndex, int size);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, a file magic value:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;data.ReadString(4, out string magic);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second is zero-terminated:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public ImmutableByteArrayReader ReadString(out string value);
public string ReadString();
public string ReadStringWithOffset(int byteIndex);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This reads until the first &lt;code&gt;0x00&lt;/code&gt; byte.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;data.ReadString(out string name);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fixed-size strings are common in headers. Zero-terminated strings are common in older binary formats, mesh files, embedded metadata, and C-style structures.&lt;/p&gt;
&lt;h3 id=&quot;writing-binary-data&quot;&gt;Writing Binary Data&lt;/h3&gt;
&lt;h4 id=&quot;binary-buffer-writer-mutablebytearraywriter&quot;&gt;Binary Buffer Writer: &lt;code&gt;MutableByteArrayWriter&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;MutableByteArrayWriter&lt;/code&gt; is the mutable writer.&lt;/p&gt;
&lt;p&gt;It is a fluent append-only byte builder:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;var current = new MutableByteArrayWriter()
    .WriteStringFixedSize(&quot;CBA1&quot;)
    .WriteInt(1)
    .WriteDouble(3.14159);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is mutable in-place. Each call appends bytes to the same underlying buffer and returns &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This makes it the practical default for producing binary files.&lt;/p&gt;
&lt;p&gt;The API deliberately uses explicit method names:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;WriteInt(...)
WriteUInt(...)
WriteDouble(...)
WriteStringFixedSize(...)
WriteStringZeroTerminated(...)
WriteBytes(...)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This avoids the ambiguity of a single overloaded &lt;code&gt;Write(...)&lt;/code&gt; method. In binary code, explicitness is usually worth the extra characters.&lt;/p&gt;
&lt;h4 id=&quot;immutable-writer-immutablebytearraywriter&quot;&gt;Immutable writer: &lt;code&gt;ImmutableByteArrayWriter&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;ImmutableByteArrayWriter&lt;/code&gt; mirrors the writer API, but every write returns a new object.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;var baseFile = new ImmutableByteArrayWriter()
    .WriteStringFixedSize(&quot;TEST&quot;)
    .WriteInt(1);

var pathA = baseFile.WriteStringZeroTerminated(&quot;Variant A&quot;);
var pathB = baseFile.WriteStringZeroTerminated(&quot;Variant B&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is useful when experimenting with different downstream write paths.&lt;/p&gt;
&lt;p&gt;For example, you may have a common header, then several possible payload layouts:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;var common = new ImmutableByteArrayWriter()
    .WriteStringFixedSize(&quot;MESH&quot;)
    .WriteInt(version);

var triangleMesh = common
    .WriteStringZeroTerminated(&quot;triangles&quot;)
    .WriteInt(triangleCount);

var pointCloud = common
    .WriteStringZeroTerminated(&quot;points&quot;)
    .WriteInt(pointCount);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The mutable writer is faster and more memory efficient. The immutable writer is better for branching, testing, and functional construction patterns.&lt;/p&gt;
&lt;h4 id=&quot;writing-fixed-size-blocks&quot;&gt;Writing fixed-size blocks&lt;/h4&gt;
&lt;p&gt;Writers also support fixed-size subblocks:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public MutableByteArrayWriter WriteBytes(int count);
public MutableByteArrayWriter WriteBytes(int count, Action&amp;lt;MutableByteArrayWriter&amp;gt; subblock);
public MutableByteArrayWriter WriteBytes(MutableByteArrayWriter source);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the mutable version:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;int versionNumber = 1;
int count = 15;

MutableByteArrayWriter current = new MutableByteArrayWriter()
    .WriteStringFixedSize(&quot;CBA1&quot;)
    .WriteBytes(32, con =&amp;gt; con.WriteUInt((uint)versionNumber))
    .WriteBytes(32)
    .WriteInt(count);

for (int i = 0; i &amp;lt; count; i++)
    current = current.WriteBytes(128);

current.Save(&quot;Copy1.bin&quot;);
current.Save(&quot;Copy2.bin&quot;);
current.Dispose();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The block continuation writes into a temporary sub-buffer. If it writes fewer than 32 bytes, the result is automatically padded with zeroes. If it writes more than 32 bytes, the writer should throw.&lt;/p&gt;
&lt;p&gt;This makes fixed-size binary structures much easier to express.&lt;/p&gt;
&lt;p&gt;The immutable version uses a functional continuation instead:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;ImmutableByteArrayWriter current = new ImmutableByteArrayWriter()
    .WriteStringFixedSize(&quot;CBA1&quot;)
    .WriteBytes(32, con =&amp;gt; con.WriteUInt((uint)versionNumber))
    .WriteBytes(32)
    .WriteInt(count);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Internally, that continuation should be shaped as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;Func&amp;lt;ImmutableByteArrayWriter, ImmutableByteArrayWriter&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;because immutable writes return new objects.&lt;/p&gt;
&lt;h3 id=&quot;examples&quot;&gt;Examples&lt;/h3&gt;
&lt;h4 id=&quot;example-reading-a-ppm-image&quot;&gt;Example: reading a PPM image&lt;/h4&gt;
&lt;p&gt;PPM is a simple image format from the Netpbm family. It is useful as a teaching format because the structure is small and direct. The examples below intentionally implement a minimal binary P6 reader and writer, rather than a complete Netpbm parser.&lt;/p&gt;
&lt;p&gt;A binary PPM file starts with an ASCII header:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;P6
width height
maxValue
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;followed by a single whitespace byte and then the binary RGB pixel data.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;P6
256 256
255
&amp;lt;binary RGB bytes&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The header is text, but the pixel payload is binary. This makes PPM a good example of mixed text and binary parsing. In the common 8-bit case where &lt;code&gt;maxValue&lt;/code&gt; is &lt;code&gt;255&lt;/code&gt;, the payload is &lt;code&gt;width * height * 3&lt;/code&gt; bytes. PPM also allows comments in the header and larger sample values, but this example deliberately supports only simple comment-free P6 files with &lt;code&gt;maxValue == 255&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A simple PPM image type might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public sealed class PpmImage
{
    public int Width { get; }
    public int Height { get; }
    public byte[] Pixels { get; }

    public PpmImage(int width, int height, byte[] pixels)
    {
        Width = width;
        Height = height;
        Pixels = pixels;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simplified parser supports only the common 8-bit RGB form. General P6 files can use larger sample values and may contain comments in the header, so a production parser would need to handle those cases before reading the raster payload. Using &lt;code&gt;ImmutableByteArrayReader&lt;/code&gt;, a basic parser could be written as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public static PpmImage ReadPpm(string path)
{
    ImmutableByteArrayReader data = ImmutableByteArrayReader.FromFile(path);

    data = ReadAsciiToken(data, out string magic);

    if (magic != &quot;P6&quot;)
        throw new InvalidDataException(&quot;Only binary P6 PPM files are supported.&quot;);

    data = ReadAsciiToken(data, out string widthText);
    data = ReadAsciiToken(data, out string heightText);
    data = ReadAsciiToken(data, out string maxValueText);

    int width = int.Parse(widthText);
    int height = int.Parse(heightText);
    int maxValue = int.Parse(maxValueText);
    data = ConsumeSingleAsciiWhitespace(data); // Skip delimiter whitespace

    if (maxValue != 255)
        throw new InvalidDataException(&quot;Only 8-bit PPM files with max value 255 are supported.&quot;);

    int pixelByteCount = width * height * 3;

    data.ReadBytes(pixelByteCount, out ImmutableByteArrayReader pixelData);

    return new PpmImage(width, height, pixelData.ToArray());
}
private static ImmutableByteArrayReader ConsumeSingleAsciiWhitespace(ImmutableByteArrayReader data)
{
    if (data.Length == 0 || !IsAsciiWhitespace(data[0]))
        throw new InvalidDataException(&quot;Expected whitespace before PPM raster data.&quot;);

    return data.Skip(1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The helper token reader can be implemented as a small routine over the immutable buffer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;private static ImmutableByteArrayReader ReadAsciiToken(ImmutableByteArrayReader data, out string token)
{
    data = SkipAsciiWhitespace(data);

    int count = 0;

    while (count &amp;lt; data.Length &amp;amp;&amp;amp; !IsAsciiWhitespace(data[count]))
        count++;

    token = Encoding.ASCII.GetString(data.AsSpan(0, count));
    return data.Skip(count);
}

private static ImmutableByteArrayReader SkipAsciiWhitespace(ImmutableByteArrayReader data)
{
    int count = 0;

    while (count &amp;lt; data.Length &amp;amp;&amp;amp; IsAsciiWhitespace(data[count]))
        count++;

    return data.Skip(count);
}

private static bool IsAsciiWhitespace(byte value)
{
    return value == (byte)' ' || value == (byte)'\t' || value == (byte)'\r' || value == (byte)'\n';
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This parser is intentionally minimal: it demonstrates the cursor-management pattern without implementing every detail of the Netpbm format. It does not support comments, alternate sample sizes, or multiple concatenated images in one file.&lt;/p&gt;
&lt;p&gt;The parser is not fighting offsets. Each helper receives a buffer and returns the remaining buffer. That is the central pattern.&lt;/p&gt;
&lt;h4 id=&quot;example-writing-a-ppm-image&quot;&gt;Example: writing a PPM image&lt;/h4&gt;
&lt;p&gt;Writing a binary PPM file is straightforward with &lt;code&gt;MutableByteArrayWriter&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public static void WritePpm(string path, int width, int height, ReadOnlySpan&amp;lt;byte&amp;gt; rgbPixels)
{
    int expectedLength = width * height * 3;

    if (rgbPixels.Length != expectedLength)
        throw new ArgumentException($&quot;Expected {expectedLength} RGB byte(s).&quot;, nameof(rgbPixels));

    new MutableByteArrayWriter()
        .WriteStringFixedSize(&quot;P6\n&quot;)
        .WriteStringFixedSize(width.ToString())
        .WriteByte((byte)' ')
        .WriteStringFixedSize(height.ToString())
        .WriteByte((byte)'\n')
        .WriteStringFixedSize(&quot;255\n&quot;)
        .WriteBytes(rgbPixels)
        .Save(path)
        .Dispose();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This writes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;P6
width height
255
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;then appends raw RGB bytes.&lt;/p&gt;
&lt;p&gt;The string methods are still explicit. We are not pretending the whole file is text. We are building a binary byte sequence with some ASCII sections.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cijf2psqvnpb6rsvk3oh.png&quot; alt=&quot;Writing Out Binary PPM P6 Format Image in Divooka and Viewing using GIMP&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can open a &lt;code&gt;.ppm&lt;/code&gt; image with GIMP.&lt;/p&gt;
&lt;p&gt;Contrast this with a traditional stream-based version:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public static void WritePpm(string path, int width, int height, ReadOnlySpan&amp;lt;byte&amp;gt; rgbPixels)
{
    int expectedLength = width * height * 3;

    if (rgbPixels.Length != expectedLength)
        throw new ArgumentException($&quot;Expected {expectedLength} RGB byte(s).&quot;, nameof(rgbPixels));

    using FileStream stream = File.Create(path);
    using BinaryWriter writer = new BinaryWriter(stream, Encoding.ASCII, leaveOpen: false);

    writer.Write(Encoding.ASCII.GetBytes(&quot;P6\n&quot;));
    writer.Write(Encoding.ASCII.GetBytes(width.ToString()));
    writer.Write((byte)' ');
    writer.Write(Encoding.ASCII.GetBytes(height.ToString()));
    writer.Write((byte)'\n');
    writer.Write(Encoding.ASCII.GetBytes(&quot;255\n&quot;));
    writer.Write(rgbPixels);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code is command-oriented, whereas &lt;strong&gt;the fluent writer makes the data structure visually continuous&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id=&quot;example-reading-a-ply-file&quot;&gt;Example: reading a PLY file&lt;/h4&gt;
&lt;p&gt;(Due to length limit of the platform, this section is not shown; Refer to &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/working-with-binary-data-in-divooka-55im&quot;&gt;https://dev.to/methodox/working-with-binary-data-in-divooka-55im&lt;/a&gt; for full article)&lt;br&gt;
(Maximum length is 20000 characters, including new lines and whitespaces.)&lt;/p&gt;
&lt;h3 id=&quot;future-improvements&quot;&gt;Future Improvements&lt;/h3&gt;
&lt;p&gt;The API can be improved in several directions.&lt;/p&gt;
&lt;p&gt;A schema layer could sit on top of these primitives. Instead of manually writing every field, a format could define named fields, block sizes, validations, and version-specific layouts.&lt;/p&gt;
&lt;p&gt;The reader could gain richer parsing helpers, such as &lt;code&gt;ReadAsciiToken&lt;/code&gt;, &lt;code&gt;ReadAsciiLine&lt;/code&gt;, &lt;code&gt;ReadUntil&lt;/code&gt;, &lt;code&gt;TryRead&lt;/code&gt;, and &lt;code&gt;Peek&lt;/code&gt; variants. These would make mixed text/binary formats like PPM and PLY even cleaner.&lt;/p&gt;
&lt;p&gt;The writer could support reservation and backpatching. Many binary formats write a length or offset before the final value is known. A controlled API for placeholders would make this safe:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;var lengthPosition = current.ReserveInt();

...

current.PatchInt(lengthPosition, payloadLength);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For very large files, both reader and writer could have stream-backed variants. &lt;code&gt;ImmutableByteArrayReader&lt;/code&gt; is ideal for in-memory data, but some workflows need lazy reading, memory-mapped files, or chunked output.&lt;/p&gt;
&lt;p&gt;The immutable writer could also use a rope-like internal representation instead of copying the whole byte array on every append. That would preserve functional semantics while making branching construction much more efficient.&lt;/p&gt;
&lt;p&gt;The core idea, though, should stay the same: binary data should be worked with as a fluent sequence of explicit operations. The API should make the structure of the format visible, while hiding the repetitive mechanics of offset movement, byte ordering, padding, and raw byte appending.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/17640/designing-a-binary-data-api-for-divooka-with-ppm-and-ply-examples</guid>
<pubDate>Tue, 12 May 2026 20:29:24 +0000</pubDate>
</item>
<item>
<title>DevLog 20260426: Divooka Mandelbrot Benchmark – Putting Our Scripting Language to the Test</title>
<link>https://coderlegion.com/15933/devlog-20260426-divooka-mandelbrot-benchmark-putting-our-scripting-language-to-the-test</link>
<description>&lt;p&gt;Today we released the first public version of &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MethodoxTech/DivookaBenchmark_Mandelbrot&quot;&gt;&lt;strong&gt;DivookaBenchmark_Mandelbrot&lt;/strong&gt;&lt;/a&gt; — a standardized benchmark suite built specifically to measure the real-world performance of Divooka against a wide range of languages and runtimes.&lt;/p&gt;
&lt;p&gt;The benchmark computes the Mandelbrot set at &lt;em&gt;2000 × 2000&lt;/em&gt; resolution with a maximum of 1000 iterations per pixel, then verifies correctness using a 32-bit checksum (target: &lt;code&gt;689833081&lt;/code&gt;). We run each implementation five times and record average time, min/max, peak memory, and distribution size.&lt;/p&gt;
&lt;h4 id=&quot;why-this-benchmark-matters&quot;&gt;Why This Benchmark Matters&lt;/h4&gt;
&lt;p&gt;One of our core beliefs is that &lt;strong&gt;a modern scripting language needs a &quot;decent (high) performance&quot;&lt;/strong&gt;. This benchmark was designed to prove that point in a reproducible way.&lt;/p&gt;
&lt;p&gt;It exercises heavy integer and floating-point math, tight loops, branches, and memory writes — exactly the kind of workload where interpretation overhead shows up quickly. This particular benchmark doesn't test function calls directly.&lt;/p&gt;
&lt;p&gt;The goal is not to produce the an optimized Mandelbrot renderer, but to compare a simple, fixed algorithm across different languages and runtimes, then use the result to understand where Divooka currently stands.&lt;/p&gt;
&lt;p&gt;We included implementations (or equivalent ports) for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C++&lt;/li&gt;
&lt;li&gt;C# (.NET 10, including AoT/trimmed)&lt;/li&gt;
&lt;li&gt;JavaScript (multiple browsers + Node.js)&lt;/li&gt;
&lt;li&gt;Go, Java, Julia, Python 3.13, Ruby, Prolog, GNU Octave&lt;/li&gt;
&lt;li&gt;And of course, multiple execution modes of Divooka 0.75.2 (compiled, &lt;strong&gt;&lt;em&gt;Aviator&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;Neo Editor&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;Stewer&lt;/em&gt;&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We even tested AI-generated versions from ChatGPT, Gemini, Grok, and Kimi to see how reliable and performant LLM-produced result is in practice.&lt;/p&gt;
&lt;h4 id=&quot;key-results-on-windows-11-i7-13700kf-&quot;&gt;Key Results (on Windows 11, i7-13700KF)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fastest overall&lt;/strong&gt;: JavaScript in Edge/Chrome/Brave (~1,296 ms, very low memory)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fastest native&lt;/strong&gt;: C++ (~1,308 ms)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strong managed contender&lt;/strong&gt;: C# AoT (~1,355 ms)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Divooka compiled&lt;/strong&gt;: 3,639 ms — comfortably beating pure interpreters like Python (~47 s) and Ruby (~49 s) ⚡&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Divooka Aviator&lt;/strong&gt;: 6,711 ms &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Divooka Neo Editor&lt;/strong&gt;: significantly higher overhead (estimated multi-hour range for full runs) &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The gap between Divooka's compiled mode and the top-tier JITs (especially &lt;strong&gt;V8&lt;/strong&gt;) is still noticeable, but the improvement over classic interpreted scripting languages is clear. This reinforces why we’ve been investing heavily in our compilation pipeline and runtime optimizations.&lt;/p&gt;
&lt;p&gt;AI-generated implementations varied wildly — some produced correct checksums but took 26 seconds to several minutes, while others failed entirely or fell back to calling Python under the hood. &lt;strong&gt;Deterministic engineering still wins.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;what-we-learned&quot;&gt;What We Learned&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;JIT is not optional for competitive scripting performance.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Tooling and editor overhead can dominate if not carefully managed (something we're actively addressing).&lt;/li&gt;
&lt;li&gt;Even simple, well-defined tasks like Mandelbrot &lt;em&gt;reveal meaningful differences in language/runtime design&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The full repository is now public:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MethodoxTech/DivookaBenchmark_Mandelbrot&quot;&gt;https://github.com/MethodoxTech/DivookaBenchmark_Mandelbrot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It includes all source implementations, PowerShell automation scripts, result tables, and artifacts. Feel free to clone, run it yourself, or contribute additional language ports.&lt;/p&gt;
&lt;p&gt;This benchmark will become part of our regular performance regression suite as Divooka evolves. Future runs should show steady gains as we refine the compiler and reduce runtime overhead.&lt;/p&gt;
&lt;h3 id=&quot;current-results&quot;&gt;Current Results&lt;/h3&gt;
&lt;p&gt;Results from best runs of respective platform:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th align=&quot;right&quot;&gt;Rank &lt;/th&gt;&lt;th&gt;Language / Runtime &lt;/th&gt;&lt;th&gt;Size &lt;/th&gt;&lt;th&gt;Best Time&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;1 &lt;/td&gt;&lt;td&gt;JavaScript Web, Edge / Chrome / Brave &lt;/td&gt;&lt;td&gt;3 KB &lt;/td&gt;&lt;td&gt;1295.80–1302.80 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;2 &lt;/td&gt;&lt;td&gt;C++ &lt;/td&gt;&lt;td&gt;15 KB &lt;/td&gt;&lt;td&gt;1308.00 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;3 &lt;/td&gt;&lt;td&gt;C# AoT &lt;/td&gt;&lt;td&gt;911 KB &lt;/td&gt;&lt;td&gt;1355.35 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;4 &lt;/td&gt;&lt;td&gt;C# .NET 10 &lt;/td&gt;&lt;td&gt;16.5 MB &lt;/td&gt;&lt;td&gt;1357.47 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;5 &lt;/td&gt;&lt;td&gt;JavaScript Node.js &lt;/td&gt;&lt;td&gt;2 KB + 98.2 MB &lt;/td&gt;&lt;td&gt;1406.20 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;6 &lt;/td&gt;&lt;td&gt;Go &lt;/td&gt;&lt;td&gt;2 KB + 224 MB &lt;/td&gt;&lt;td&gt;1425.01 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;7 &lt;/td&gt;&lt;td&gt;Julia &lt;/td&gt;&lt;td&gt;2 KB + 1 GB &lt;/td&gt;&lt;td&gt;1511.20 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;8 &lt;/td&gt;&lt;td&gt;JavaScript Web, Firefox &lt;/td&gt;&lt;td&gt;3 KB &lt;/td&gt;&lt;td&gt;1513.00 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;9 &lt;/td&gt;&lt;td&gt;Java OpenJDK 17.0.11 &lt;/td&gt;&lt;td&gt;301 MB + small files &lt;/td&gt;&lt;td&gt;1587.79 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;10 &lt;/td&gt;&lt;td&gt;Divooka 0.75.2, Compiled &lt;/td&gt;&lt;td&gt;71.914 MB &lt;/td&gt;&lt;td&gt;3639.24 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;11 &lt;/td&gt;&lt;td&gt;Divooka 0.75.2, Aviator Run &lt;/td&gt;&lt;td&gt;8 KB + 2.16 GB + 776 MB &lt;/td&gt;&lt;td&gt;6710.84 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;12 &lt;/td&gt;&lt;td&gt;Python 3.13.5 &lt;/td&gt;&lt;td&gt;2 KB + 139 MB &lt;/td&gt;&lt;td&gt;47127.70 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;13 &lt;/td&gt;&lt;td&gt;Ruby 3.2.2 &lt;/td&gt;&lt;td&gt;1 KB + 907 MB &lt;/td&gt;&lt;td&gt;49489.82 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;14 &lt;/td&gt;&lt;td&gt;Prolog SWI, optimized &lt;/td&gt;&lt;td&gt;2 KB + 42.8 MB &lt;/td&gt;&lt;td&gt;314687.84 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;15 &lt;/td&gt;&lt;td&gt;GNU Octave 7.3.0 &lt;/td&gt;&lt;td&gt;2 KB + 2.07 GB &lt;/td&gt;&lt;td&gt;2464913.29 ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td align=&quot;right&quot;&gt;16 &lt;/td&gt;&lt;td&gt;Divooka 0.75.2, Neo Editor estimate &lt;/td&gt;&lt;td&gt;8 KB + 2.67 GB &lt;/td&gt;&lt;td&gt;~95.8 hr&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;limitations&quot;&gt;Limitations&lt;/h3&gt;
&lt;p&gt;This benchmark only tests a narrow case.&lt;/p&gt;
&lt;p&gt;It does not test UI programming, data manipulation, graph execution, external libraries, async workloads, recursion-heavy workloads, or large real-world applications.&lt;/p&gt;
&lt;p&gt;It also does not fully normalize warm-up behavior. Browser JavaScript likely benefits from highly optimized JIT behavior, and other runtimes may have different startup / warm-up costs.&lt;/p&gt;
&lt;p&gt;GNU Octave is probably optimized for matrix-oriented workloads, not naive scalar loops.&lt;/p&gt;
&lt;p&gt;Python is rarely used this way in performance-sensitive numerical code; normally people use NumPy, Numba, Cython, PyPy, or native extensions.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/15933/devlog-20260426-divooka-mandelbrot-benchmark-putting-our-scripting-language-to-the-test</guid>
<pubDate>Mon, 27 Apr 2026 03:33:55 +0000</pubDate>
</item>
<item>
<title>2026 Divooka™ Explore Steam Release Trailer</title>
<link>https://coderlegion.com/15699/2026-divooka-explore-steam-release-trailer</link>
<description>&lt;p&gt;Divooka is a high-performance visual programming platform designed for creators, developers, and world builders. Build applications, generate worlds, process data, and experiment with computation using a powerful node-based system combined with flexible code-driven workflows.&lt;/p&gt;
</description>
<category>Videos</category>
<guid isPermaLink="true">https://coderlegion.com/15699/2026-divooka-explore-steam-release-trailer</guid>
<pubDate>Fri, 24 Apr 2026 02:34:45 +0000</pubDate>
</item>
<item>
<title>DevLog 20260319: Towards OOP - Document-Level Default Module and Module-Level Members and Behaviors</title>
<link>https://coderlegion.com/13227/devlog-20260319-towards-oop-document-level-default-module-module-level-members-behaviors</link>
<description>&lt;h3 id=&quot;revisiting-flappy-bird-the-pain-point&quot;&gt;Revisiting Flappy Bird – The Pain Point&lt;/h3&gt;
&lt;p&gt;Remember our Flappy Bird example from mid-2025?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/509yykxr054pazr7crm6.png&quot; alt=&quot;The Initialization Sequence for Flappy Bird Game in Divooka Interactive Demo&quot;&gt;&lt;/p&gt;
&lt;p&gt;The long chain of variable initializations was needed for two main reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;No way to declare variables before using them  &lt;/li&gt;
&lt;li&gt;More critically - &lt;strong&gt;no way to define custom data structures&lt;/strong&gt; that could group related values together and be instantiated with a single node.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In retrospective it seems obvious: we just need proper structured data containers. But building the scaffolding to support this properly in a visual, node-based system is a bit more involved.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c044pu9pmmgtppisnts7.png&quot; alt=&quot;Initialize State with A Single Node with Structured Data&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;searching-for-a-solution&quot;&gt;Searching for a Solution&lt;/h3&gt;
&lt;p&gt;I spent a long time trying to bring OOP concepts into Divooka. A huge amount of effort went into DiOS (Divooka Open Standards), with the hope that a complete standard would naturally solve these kinds of problems. But standardization work is slow and deep - just like making Divooka itself feature-complete.&lt;/p&gt;
&lt;p&gt;Then, while working on something completely unrelated and taking a short break from this topic, it suddenly clicked.&lt;/p&gt;
&lt;p&gt;The key insight: &lt;strong&gt;start simple and approach it from a functional/declarative angle first&lt;/strong&gt;, before jumping into inheritance, encapsulation, polymorphism, and all those good stuff.&lt;/p&gt;
&lt;p&gt;I'm really excited - we finally have meaningful progress.&lt;/p&gt;
&lt;h3 id=&quot;the-divooka-way&quot;&gt;The Divooka Way&lt;/h3&gt;
&lt;p&gt;To claim our goal as &quot;everything is a node&quot; is a bit too strong, but I strongly prefer keeping almost everything on the graph.&lt;/p&gt;
&lt;p&gt;This aligns with how we already handle &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20260225-divooka-visual-programming-direct-value-setting-of-compound-inputs-4dej&quot;&gt;structured primitives&lt;/a&gt; - direct value setting of compound inputs. That way, there's still hope that users can drop down to raw node programming when they need full control (and thus keep everything programmable).&lt;/p&gt;
&lt;p&gt;Traditional property panels (like Unreal Blueprints' object definition GUIs) are out - they break the pure graph paradigm.&lt;/p&gt;
&lt;p&gt;Instead, the solution is declarative, similar to how TerraGen works: &lt;strong&gt;the mere presence and connection of certain nodes defines meaning&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dt0uo32f876mkby6xfce.png&quot; alt=&quot;Custom Module Data Member Definition in Divooka&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q5rcgs9pioy63whstj6r.png&quot; alt=&quot;Declarative Programming in Divooka in the Context of Module Member Definitions&quot;&gt;&lt;/p&gt;
&lt;p&gt;This feels &lt;strong&gt;awesome&lt;/strong&gt; for several reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We can make full use of the 2D graph layout - place things exactly where we want them visually  &lt;/li&gt;
&lt;li&gt;It stays completely coherent with Divooka's graphical paradigm  &lt;/li&gt;
&lt;li&gt;No need for separate &quot;class editor&quot; UIs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;what-s-been-implemented-so-far&quot;&gt;What's Been Implemented So Far&lt;/h3&gt;
&lt;p&gt;Current working pieces:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Graphs can now act as &lt;strong&gt;modules&lt;/strong&gt;  &lt;/li&gt;
&lt;li&gt;Support for &lt;strong&gt;module-level data members&lt;/strong&gt; and &lt;strong&gt;behavior members&lt;/strong&gt;  &lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;main module&lt;/strong&gt; tied to the document, with existing graphs serving as associated behaviors&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Module-scoped instance node – in Events context&lt;/strong&gt;&lt;br&gt;
&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4fuz1d4xkpggewvio85i.png&quot; alt=&quot;Module Scoped Instance Node in Divooka - Events Context&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Module-scoped instance node – in Dataflow context&lt;/strong&gt;&lt;br&gt;
&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3it8f0cg1weluxbjb9uq.png&quot; alt=&quot;Module Scoped Instance Node in Divooka - Dataflow Context&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Top-level module behavior graphs&lt;/strong&gt;&lt;br&gt;
&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/54bhw9ol26pp6265hjri.png&quot; alt=&quot;Top Level Module Behavior Member Graphs in Divooka&quot;&gt;&lt;/p&gt;
&lt;p&gt;This immediately unlocks:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clean access to (top-level) module instances  &lt;/li&gt;
&lt;li&gt;Shared event and dataflow instance data  &lt;/li&gt;
&lt;li&gt;A unified programming and document model for subgraphs (still a work-in-progress) &lt;/li&gt;
&lt;li&gt;In procedural contexts (&lt;strong&gt;Glaze!&lt;/strong&gt; especially): we now have proper data containers - no more awkward giant variable lists or forced tuples! (Compare to the old Flappy Bird screenshot)  &lt;/li&gt;
&lt;li&gt;Much simpler and more natural state storage/access in &lt;strong&gt;Glaze!&lt;/strong&gt; and procedural graphs&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is a big deal - for a system that's gearing up for real production use, it feels like crossing a major milestone in usefulness and core abstractions.&lt;/p&gt;
&lt;h3 id=&quot;current-limitations-amp-remaining-work&quot;&gt;Current Limitations &amp;amp; Remaining Work&lt;/h3&gt;
&lt;p&gt;Still plenty to fix/polish:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A bunch of bugs and edge cases  &lt;/li&gt;
&lt;li&gt;Proper serialization support  &lt;/li&gt;
&lt;li&gt;Smooth context switching between graphs  &lt;/li&gt;
&lt;li&gt;Subgraph consolidation / better nesting UX  &lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;looking-ahead&quot;&gt;Looking Ahead&lt;/h3&gt;
&lt;p&gt;This is a solid foundation, but there's still a long road:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Full modularization support  &lt;/li&gt;
&lt;li&gt;Modules inside modules (nesting)  &lt;/li&gt;
&lt;li&gt;Module inheritance / composition patterns / polymorphism&lt;/li&gt;
&lt;li&gt;Base DOM exposed as the &quot;main module&quot; for metaprogramming API  &lt;/li&gt;
&lt;li&gt;Detailed documentation: how it all fits together, recommended usage patterns, expected behaviors, pitfalls  &lt;/li&gt;
&lt;li&gt;Module-level scoped instances (deeper/finer control)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall - I am very happy with the direction. It feels like Divooka is finally getting the abstraction power it deserves while staying true to its visual, graph-first philosophy.&lt;/p&gt;
&lt;p&gt;I am hyped for what we will achieve.&lt;/p&gt;
&lt;h3 id=&quot;references-1&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Frameworks/Glaze/Tutorials/FlappyBird&quot;&gt;The original flappy bird example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250711-flappy-bird-in-divooka-sneak-peak-40j8&quot;&gt;DevLog on flappy bird example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/13227/devlog-20260319-towards-oop-document-level-default-module-module-level-members-behaviors</guid>
<pubDate>Thu, 19 Mar 2026 20:31:07 +0000</pubDate>
</item>
<item>
<title>DevLog 20260225: Divooka Visual Programming - Direct Value Setting of Compound Inputs</title>
<link>https://coderlegion.com/12185/devlog-20260225-divooka-visual-programming-direct-value-setting-of-compound-inputs</link>
<description>&lt;p&gt;Finally, it arrived! Well, not exactly here yet, but getting closer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9uefmdr5tir0qjdcfyax.png&quot; alt=&quot;Screenshot of Visual Programming in Divooka - Value Assignment of Compound Inputs&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;overview-1&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;Divooka intends to make visual programming simple and accessible - the accessible part is achieved both through wide availability and the general-purpose nature of the programming system, but the &lt;em&gt;simple&lt;/em&gt; part is what we spend lots of effort thinking hard trying to improve.&lt;/p&gt;
&lt;p&gt;In the past, dealing with complex configuration takes the form of either &lt;em&gt;&quot;explosion&quot;&lt;/em&gt; of arguments or dedicated &lt;code&gt;Make XXX&lt;/code&gt; construction functions:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mf6epcnl90nzwn4v3sni.png&quot; alt=&quot;An Example of A Node with Lots of Inputs&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/haamwdqh83lph8wphcw4.png&quot; alt=&quot;An Example of A Node Taking a Struct, Which Needs to be Created from a Node with Lots of Inputs&quot;&gt;&lt;/p&gt;
&lt;p&gt;The problem arises due to direct exposure of all input arguments in visual programming. Both approach makes the canvas look busy, with the former a common approach in existing systems - using a struct sounds good on paper but still requires a complex looking construction node. This is a common problem in visual programming systems, below shows how it's done in Blender and ComfyUI.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p74kj1nn0v5clwya2bp4.png&quot; alt=&quot;A Node with Many Inputs in ComfyUI&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5jkcps43hasucbgc4fjr.png&quot; alt=&quot;A Node with Many Inputs in Blender&quot;&gt;&lt;/p&gt;
&lt;p&gt;You do get used to it after a while, but a busy looking canvas remains a busy-looking canvas. In Blender, you can sort of &quot;collapse&quot; a node:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/51ssein4u54ak1b4x2dp.png&quot; alt=&quot;Collapse Nodes in Blender&quot;&gt;&lt;/p&gt;
&lt;p&gt;As Blender is constantly improving, we can also now collapse property groups as shown in the earlier screenshot.&lt;/p&gt;
&lt;p&gt;As a syntax purist and (unfortunately) very conscious about my working space, my eyes can't tolerate messiness. I kind of like how Houdini approaches this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6rcfmxoty5i6dt915ue9.png&quot; alt=&quot;Nodes in Houdini Are Very Simple and Clean&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/25jwxr7qrhzxak2u6ylm.png&quot; alt=&quot;Houdini Makes Use of Property Panels&quot;&gt;&lt;/p&gt;
&lt;p&gt;Essentially they offload all the complexity to the properties panel - this has the huge benefit of making everything look very clean. But the downside is Houdini rely heavily on scripting expressions (like Excel).&lt;/p&gt;
&lt;p&gt;TerraGen and World Machine mixes the approach:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8j6701vpaacarx9406jj.png&quot; alt=&quot;A Node Graph in World Machine&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fabvq61w0sqm3oiybyxf.png&quot; alt=&quot;A Node Graph in TerraGen&quot;&gt;&lt;/p&gt;
&lt;p&gt;In both cases, connections represent flow of terrain data only and additional configurations are offloaded to property panels. In TerraGen especially, this allows those properties to be animated. The downside, once again, is &lt;strong&gt;whatever is offloaded to property panels are no longer programmable using nodes&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;our-approach&quot;&gt;Our Approach&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9uefmdr5tir0qjdcfyax.png&quot; alt=&quot;Compound Input Editing Popup in Divooka&quot;&gt;&lt;/p&gt;
&lt;p&gt;Our proposed solution for Divooka looks like this: fundamentally all properties remain as node inputs, but compound values (especially value types) should just allow &quot;editing in place&quot; through a property panel. If more complex programming is needed, one can easily fall back to &lt;code&gt;Make XXX&lt;/code&gt; nodes again.&lt;/p&gt;
&lt;h3 id=&quot;future-work&quot;&gt;Future Work&lt;/h3&gt;
&lt;p&gt;There is still remaining work on type check, hierarchical compound types, arrays, and serialization, and also node rendering.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-2&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;As usual with all the exciting language features of Divooka we intend to introduce, there are lots of details to take care of here.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/12185/devlog-20260225-divooka-visual-programming-direct-value-setting-of-compound-inputs</guid>
<pubDate>Wed, 25 Feb 2026 17:55:52 +0000</pubDate>
</item>
<item>
<title>DevLog 20260224 Divooka - Node-Level Automatic Dispatch (Runtime Execution Behavior)</title>
<link>https://coderlegion.com/12155/devlog-20260224-divooka-node-level-automatic-dispatch-runtime-execution-behavior</link>
<description>&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k0nmv1dzv1kuerx8vvr5.png&quot; alt=&quot;Scalar Operation&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;overview-2&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;Anything useful done with a program eventually involves file I/O and some form of repetition.&lt;/p&gt;
&lt;p&gt;In Divooka, the dataflow context already makes it very easy to &quot;just get a single thing done.&quot; That part feels good. But handling loops? That's where things start to get awkward.&lt;/p&gt;
&lt;p&gt;One challenge lies on the GUI side — especially around lambdas and subgraphs. We do have foundational support (as shown &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250510-dealing-with-lambda-3ff9&quot;&gt;here&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/progress-share-graph-local-lambda-calculus-4dj2&quot;&gt;here&lt;/a&gt;), but let's be honest: it's not &lt;em&gt;smooth&lt;/em&gt; yet.&lt;/p&gt;
&lt;p&gt;So the question becomes: how do we make repetition feel natural without introducing heavy conceptual overhead?&lt;/p&gt;
&lt;h3 id=&quot;the-plan&quot;&gt;The Plan&lt;/h3&gt;
&lt;p&gt;There are several well-established ways to achieve looping behavior in a functional or visual programming context:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Subgraph context&lt;/strong&gt; - Used in tools like Blender, vvvv, and Houdini. A specialized node group or frame defines loop entry and exit boundaries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lambda callbacks&lt;/strong&gt; - Evaluation is handled via callback-style execution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expressive recursion&lt;/strong&gt; - As seen in text-based functional languages, requiring explicit termination conditions to avoid stack overflow.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All of these approaches are powerful and necessary in certain contexts. But they also introduce additional constructs into the language model. For advanced workflows, that's fine. For simple operations, it can feel unnecessarily complex.&lt;/p&gt;
&lt;p&gt;I wanted something lighter.&lt;/p&gt;
&lt;h3 id=&quot;array-coercion&quot;&gt;Array Coercion&lt;/h3&gt;
&lt;p&gt;Divooka already supports &lt;strong&gt;Array Coercion&lt;/strong&gt; — a scalar value can be passed into an input expecting a collection.&lt;/p&gt;
&lt;p&gt;This avoids the need to manually &quot;wrap&quot; a scalar into an array just to satisfy a function signature - I still remember the pain of handling arrays in Unreal Blueprint.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y2m2p8hs7ellxbfzgj5m.png&quot; alt=&quot;Treat Scalar as Array&quot;&gt;&lt;/p&gt;
&lt;p&gt;Scalar-to-array coercion keeps graphs clean and avoids clustering.&lt;/p&gt;
&lt;p&gt;To take this to the next level, I've been giving it much thought on how the reverse might work: if we can coerce scalars into arrays — what if we also let nodes automatically dispatch when arrays are passed in?&lt;/p&gt;
&lt;h3 id=&quot;automatic-dispatch-node-level-&quot;&gt;Automatic Dispatch (Node-Level)&lt;/h3&gt;
&lt;p&gt;The core design question was:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Should an array input implicitly turn the &lt;em&gt;entire downstream chain&lt;/em&gt; into a loop?&lt;/li&gt;
&lt;li&gt;Or should the dispatch behavior stay localized to the node itself?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eventually it seemed localizing things to the node made far more sense. &lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;string String.Replace(string, string, string)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If any input is provided as an array instead of a scalar, the node automatically promotes itself to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;string[] String.Replace(string[], string[], string[])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The node executes element-wise.&lt;/p&gt;
&lt;p&gt;Now, I can rename a folder of files with roughly three nodes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enumerate files&lt;/li&gt;
&lt;li&gt;Regex replace&lt;/li&gt;
&lt;li&gt;Rename file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No explicit loop node. No subgraph. No lambdas.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7bvz2dbw56ctqp2kju5n.png&quot; alt=&quot;Dispatch Setup&quot;&gt;&lt;/p&gt;
&lt;p&gt;In this example, &lt;code&gt;Duplicate as Array&lt;/code&gt; returns a strongly typed &lt;code&gt;string[]&lt;/code&gt;, enabled by &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250710-generics-in-divooka-49e6&quot;&gt;generics support&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One important rule: All array inputs must align in size with the source array. The dispatch is index-based and deterministic.&lt;/p&gt;
&lt;h3 id=&quot;full-example&quot;&gt;Full Example&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tsypgdt3xrj6g4phkvij.png&quot; alt=&quot;Full Example&quot;&gt;&lt;/p&gt;
&lt;p&gt;What used to require explicit iteration logic now becomes implicit behavior at the node boundary. The graph remains readable. The mental model stays simple.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-3&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This is still early work. Integration with the rest of Divooka's features is ongoing, and edge cases are being worked through.&lt;/p&gt;
&lt;p&gt;So far so good.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/12155/devlog-20260224-divooka-node-level-automatic-dispatch-runtime-execution-behavior</guid>
<pubDate>Tue, 24 Feb 2026 22:22:17 +0000</pubDate>
</item>
<item>
<title>Weekly Update 2026-01-11</title>
<link>https://coderlegion.com/9555/weekly-update-2026-01-11</link>
<description>&lt;p&gt;This week was mostly about tightening up the foundations of our tools rather than pushing new features. A lot of our older experiments were starting to show their age, so we spent time cleaning things up and getting the core systems into better shape.&lt;/p&gt;
&lt;h4 id=&quot;what-happened-this-week&quot;&gt;What Happened This Week&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Refactoring Neo + Parcel NExT review&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We continued working through refactors on Neo and reviewing the Parcel NExT implementation. Some of the earlier experimental frontend work had spread across multiple assemblies, so small API changes ripple everywhere. Slow, but necessary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WPF + Nodify wrestling&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A surprising amount of time went into understanding how WPF resource dictionaries, theming, and Nodify's API actually want to behave together. After digging through the quirks, we finally have a clearer picture of how to keep styling and view models consistent. Didn't get as far into custom styling as we hoped, but at least the fundamentals are sorted out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Serialization groundwork&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Started wiring up the basics for text-based JSON serialization of Divooka graphs/documents. Also began work on package reference records and figuring out how packages get identified and loaded at runtime. All early steps, but important ones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Graph editor + GUI cleanup&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Made some initial passes at reorganizing the graph editor. Small changes now, but they'll help us build better tools on top later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fantasy Planet Painter update&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Spent a little time polishing and updating the Steam submission for Fantasy Planet Painter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Methodox Threads&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We also pushed out an early public release of &lt;strong&gt;Methodox Threads&lt;/strong&gt;, our lightweight branching text environment for managing non-linear AI conversations. The new v0.7 build adds integrated Gen-AI generation directly inside each document pane, configurable providers, and a simple JSON-backed structure for exporting or versioning thought trees. It's essentially a cleaner way to explore tangents, run parallel prompts, and keep complex LLM work organized. Download is available on &lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox.itch.io/threads&quot;&gt;itch.io&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;closing-thoughts&quot;&gt;Closing Thoughts&lt;/h4&gt;
&lt;p&gt;Not a &quot;big features&quot; week, but a surprisingly productive one in terms of future stability. Refactors aren't glamorous, but they make everything that comes next a lot smoother.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/9555/weekly-update-2026-01-11</guid>
<pubDate>Mon, 12 Jan 2026 01:29:36 +0000</pubDate>
</item>
<item>
<title>DevLog 20250507 Bringing Smart Suggestions and Smarter Search to Divooka’s Visual Coding</title>
<link>https://coderlegion.com/3063/devlog-20250507-bringing-smart-suggestions-and-smarter-search-to-divookas-visual-coding</link>
<description>&lt;h3 id=&quot;learning-from-unreal-engine-blueprints&quot;&gt;Learning from Unreal Engine Blueprints&lt;/h3&gt;
&lt;p&gt;I’ve always admired Unreal Engine’s Blueprints - particularly its subtle yet powerful &lt;strong&gt;connection suggestion feature&lt;/strong&gt;. When you drag a wire out from a pin, the system intuitively suggests compatible nodes based on the pin’s data type.&lt;/p&gt;
&lt;p&gt;This kind of context-awareness accelerates creation and keeps workflow fluid by making things &lt;em&gt;just work&lt;/em&gt; - no guesswork needed.&lt;/p&gt;
&lt;h3 id=&quot;the-challenge-missing-type-labels-in-c-instance-methods&quot;&gt;The Challenge: Missing Type Labels in C# Instance Methods&lt;/h3&gt;
&lt;p&gt;Divooka, built atop C#, posed a snag: &lt;strong&gt;instance methods don’t include the object’s type in their signature&lt;/strong&gt;. So if a method like &lt;code&gt;Rotate()&lt;/code&gt; belongs to a &lt;code&gt;Transform&lt;/code&gt; object, the type isn’t explicitly declared in the method signature - making automatic suggestion tricky.&lt;/p&gt;
&lt;p&gt;To match Unreal’s intuitiveness, I needed a way to tell Divooka - not just &quot;Rotate()&quot;, but &lt;em&gt;Rotate() on Transform&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&quot;explicit-type-filter-in-search-syntax&quot;&gt;Explicit Type Filter in Search Syntax&lt;/h3&gt;
&lt;p&gt;The first step is to allow users to filter the toolbox explicitly in their search query.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r0n5z8by0eapb5k7iy4t.png&quot; alt=&quot;Instance Function without Type Parameter&quot;&gt;&lt;/p&gt;
&lt;p&gt;For example: &lt;code&gt;[Pixel Image] Save&lt;/code&gt; would filter for methods in the &lt;code&gt;Pixel Image&lt;/code&gt; toolbox (category). The symmetry of this syntax remains intuitive even in localized languages like Chinese - because only the type would be translated, not the syntax design itself.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g1girqglhmc2hx0itcuo.png&quot; alt=&quot;Type Parameter Syntax&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;building-the-pipeline-filtering-and-highlighting&quot;&gt;Building the Pipeline: Filtering and Highlighting&lt;/h3&gt;
&lt;p&gt;Next, I leveled up the search experience by crafting a multi-stage pipeline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Category names&lt;/strong&gt; (like &lt;code&gt;[AI Service]&lt;/code&gt;, &lt;code&gt;[Pixel Image]&lt;/code&gt;) now at the front, so users can scope their search by purpose.&lt;/li&gt;
&lt;li&gt;Then we optionally parse &lt;code&gt;&amp;lt;TypeName&amp;gt;&lt;/code&gt; syntax, which is used to filter input parameters.&lt;/li&gt;
&lt;li&gt;Finally, full-text search for other keywords.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So a query might look like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Pixel Image] &amp;lt;String&amp;gt; Save
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In which case we will search only for functions containing the word &quot;Save&quot; and expects a &lt;code&gt;String&lt;/code&gt; parameter. Visually, I emphasized that first category tag with syntax highlighting to help the eye quickly scan.&lt;/p&gt;
&lt;h3 id=&quot;the-result-it-just-works-mostly-&quot;&gt;The Result: It Just Works (Mostly)&lt;/h3&gt;
&lt;p&gt;Here’s how it currently looks:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kw31ux4qf0ajmc7oneq9.gif&quot; alt=&quot;Working Result&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Type &lt;code&gt;[Pixel Image] Save&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The search autocomplete suggests all instance methods named &lt;code&gt;Save...()&lt;/code&gt; defined in &lt;code&gt;Pixel Image&lt;/code&gt; toolbox (mostly operating on the &lt;code&gt;PixelImage&lt;/code&gt; object type) (e.g., &lt;code&gt;PixelImage.Save&lt;/code&gt;, &lt;code&gt;PixelImage.Load&lt;/code&gt;, etc.).&lt;/li&gt;
&lt;li&gt;Everything lights up in palette instantly - &lt;em&gt;users can find what they need fast&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;what-s-missing-auto-connecting-suggestions&quot;&gt;What’s Missing: Auto-Connecting Suggestions&lt;/h3&gt;
&lt;p&gt;This solves the &lt;strong&gt;searching&lt;/strong&gt; half of the problem beautifully - but it doesn’t yet auto-connect. In Unreal, dragging from a &lt;code&gt;Vector&lt;/code&gt; pin might instantly suggest &lt;code&gt;Normalize()&lt;/code&gt; - and connect it for you if you click that suggestion.&lt;/p&gt;
&lt;p&gt;Right now, Divooka still requires a manual drag-and-click - our intention was only searching and filtering, not full drag-and-drop automation.&lt;/p&gt;
&lt;h3 id=&quot;next-steps-toward-full-contextual-suggestions&quot;&gt;Next Steps: Toward Full Contextual Suggestions&lt;/h3&gt;
&lt;p&gt;I’m plotting the future:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pin-aware suggestion&lt;/strong&gt;: When dragging a wire from a pin, immediately filter down to matching methods—and optionally even pre-wire the node when hitting “Enter” or double-clicking.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type inference&lt;/strong&gt;: Avoid requiring &lt;code&gt;&amp;lt;Type&amp;gt;&lt;/code&gt; if the context is obvious - since the system already knows the pin’s type.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better localization&lt;/strong&gt;: Make sure category tags and type filters localize cleanly, without mangling grammar in languages like Chinese, Japanese, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced UX hints&lt;/strong&gt;: Perhaps grey-out non-matching nodes live, or provide quick tooltips (e.g. “only shows methods on Transform”, like Unreal Blueprint's node search interface).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Previously, instance-based methods don’t expose their object type; this hampers auto-suggestion. We solved this by introducing explicit &lt;code&gt;&amp;lt;Type&amp;gt;&lt;/code&gt; filters and category tags in our search syntax. The result is now the users can quickly locate relevant methods with filtered search - instant clarity. Up next, we want to add full context-aware, auto-connecting suggestion - move from “search” to “smart connection.”&lt;/p&gt;
&lt;h3 id=&quot;conclusion-4&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Unreal Blueprints’ delightful node suggestions inspired us to refine Divooka’s search. By introducing syntax like &lt;code&gt;[Category] &amp;lt;Type&amp;gt; Keywords...&lt;/code&gt;, Divooka now lets you filter instance-based methods explicitly - greatly improving discoverability. Coming up: auto-connection and smarter, context-aware wiring.&lt;/p&gt;
&lt;p&gt;Let me know if you are interesting in learning more specific implementation details - like the regex matching, indexing logic, or localization strategy!&lt;/p&gt;
&lt;p&gt;Happy visual coding!&lt;/p&gt;
</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/3063/devlog-20250507-bringing-smart-suggestions-and-smarter-search-to-divookas-visual-coding</guid>
<pubDate>Sun, 07 Sep 2025 23:39:02 +0000</pubDate>
</item>
<item>
<title>[Article] Divooka, Where We Are</title>
<link>https://coderlegion.com/4760/article-divooka-where-we-are</link>
<description>&lt;blockquote&gt;&lt;p&gt;Many years from now, when power users, researchers, and historians look at this article, they will see how it all started and glimpse the vision behind our work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;overview-3&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;Ah, vision - a fascinating subject, and a quality not appreciated by all. Like the ability to dream, to imagine, and to dwell in the realm of mathematics and technicality. For imaginative minds, it's dangerous to wander too far; without standing on solid ground, one can hardly make real progress. Often, it pays to be hands-on.&lt;/p&gt;
&lt;p&gt;Divooka is (or is becoming) a complete, general-purpose, high-level, multi-paradigm, flexibly typed, modern visual programming language/system, complete with package management, standard libraries, application frameworks, graph editors, IDE, CLI executor, code generator &amp;amp; compiler, derived frontend apps, examples, documentation, and accompanying tutorials and training materials. We've achieved the majority of this already, so it's not an exaggeration to list them now. What's more, there are still ideas I haven't put into words.&lt;/p&gt;
&lt;p&gt;It's hard to do one thing well, and to achieve all of this with limited resources, we need a good strategy. Of course, the strategy itself is a secret I won't share - it's fairly simple, really, but requires determination and execution power. After a year since incorporation last July, it's useful to review and recap what we've done and where we are.&lt;/p&gt;
&lt;p&gt;In this article, I'll focus on the period from January to August 2025, but to give a complete picture, I find it instructive to look back and highlight outcomes from last year and key lessons learned.&lt;/p&gt;
&lt;h3 id=&quot;early-2024&quot;&gt;Early 2024&lt;/h3&gt;
&lt;p&gt;2024 was about the modernized inception of what Divooka is today.&lt;/p&gt;
&lt;p&gt;Neo was reviewed a third time, and we managed to trim down most legacy code. It was time to think more broadly about the concept of &quot;general-purpose visual programming,&quot; rather than just a demonstration for data analytics. A new repository was created for modern development. &lt;strong&gt;&lt;em&gt;Gospel&lt;/em&gt;&lt;/strong&gt; was conceived.&lt;/p&gt;
&lt;p&gt;Talks with investors and prospective entrepreneurs mostly confirmed that this subject is hard, and we learned there are countless low-code platforms in China. That is good.&lt;/p&gt;
&lt;p&gt;A disagreement with a co-creator helped me settle the fact that we would definitely go desktop-first and worry about the web later.&lt;/p&gt;
&lt;p&gt;The conception of &lt;strong&gt;&lt;em&gt;Quadrinity&lt;/em&gt;&lt;/strong&gt; and the flexible office software framework inspired me to quit my job and move forward.&lt;/p&gt;
&lt;h3 id=&quot;july-sep-2024&quot;&gt;July–Sep 2024&lt;/h3&gt;
&lt;p&gt;During initial incorporation, development was highly focused, and Divooka, though very simplistic, was a well-defined, easy-to-perceive single tool with only a handful of nodes. With Haopu's help on plotting, it quickly looked like a handy little utility for data analytics - &lt;strong&gt;a subject matter we should continue to explore and focus on in the future&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;At the same time, to prepare for public preview and an angel investor meeting, I spent a month making prototypes for many potential application areas, including experimental libraries and web use. Due to lack of sophisticated integration and completeness, I doubt these prototypes made as strong an impression as I hoped.&lt;/p&gt;
&lt;p&gt;On the marketing side, the concept of a case competition was pitched during this time, but it never materialized later in 2024 due to slow progress on software completion and the dashboard library.&lt;/p&gt;
&lt;h3 id=&quot;sep-dec-2024&quot;&gt;Sep–Dec 2024&lt;/h3&gt;
&lt;p&gt;With the promise of investor funding, I decided to expand operations and start hiring contractors to work on various aspects of the overall Divooka platform, including GUI design, website improvements, and preparation for first practical use. In a &lt;a rel=&quot;nofollow&quot; href=&quot;https://medium.methodox.io/divooka-my-vision-for-the-future-of-visual-programming-99b4b11ff9b6&quot;&gt;previous article&lt;/a&gt;, I mentioned that end users were unlikely to use Divooka directly during initial adoption. To elaborate:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Lack of comprehensive DSL libraries at this stage.&lt;/li&gt;
&lt;li&gt;Overall complexity and learning curve as a general-purpose programming language.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;While we could have focused on DSL, the second point made it unlikely to gain traction (given how unknown we were) and not commercially feasible any time soon. Thus, we needed more specific, tool-like, highly focused, direct end-user GUI applications.&lt;/p&gt;
&lt;p&gt;For that reason, we published &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox.itch.io/fantasy-planet-painter&quot;&gt;Fantasy Planet Painter&lt;/a&gt;&lt;/strong&gt;. We also planned to publish Arcadia, but timeline issues prevented it.&lt;/p&gt;
&lt;p&gt;It was during this period that we conceived what was previously known as &lt;strong&gt;&quot;ZFA,&quot;&lt;/strong&gt; while &quot;fighting&quot; for the trademark of &quot;Zora&quot; in Canada. Due to delays with investment funding, personnel management, and other corporate matters, we didn't make significant progress on the modern &lt;strong&gt;Morpheus&lt;/strong&gt; framework or ZFA at that time.&lt;/p&gt;
&lt;p&gt;In conclusion, during these busy four months, there was not much I would call technical progress. Most efforts were miscellaneous but time-consuming. I also learned it was better to take more matters into my own hands.&lt;/p&gt;
&lt;h3 id=&quot;jan-aug-2025&quot;&gt;Jan–Aug 2025&lt;/h3&gt;
&lt;p&gt;January to August has been much more fruitful and productive, despite major distractions in January, May, and July. With help from Aaron, Allan, and ChatGPT - and without the communication burden of contractors and external collaborators - progress has been slow but steady. More importantly, I've had time to investigate highly technical matters and build foundational work.&lt;/p&gt;
&lt;p&gt;The Web Summit in May and internal training from April to July helped us greatly polish the Neo graph editor, stabilize Neo's internal serialization format, and establish Divooka for general-purpose use.&lt;/p&gt;
&lt;p&gt;Ecosystem-wise, I've done plenty of investigation and prototype work on Python integration, build automation, code refactoring, and preliminary work on online services.&lt;/p&gt;
&lt;p&gt;There has been extensive work on Neo and Neo-first features, including general GUI improvements, bug fixes, plugin framework enhancements, &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250728-multi-graph-navigation-gui-2h08&quot;&gt;subgraph work&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250710-generics-in-divooka-49e6&quot;&gt;generics&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250510-dealing-with-lambda-3ff9&quot;&gt;lambda support&lt;/a&gt;, multilingual framework setup, &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250708-procedural-context-visual-debugger-in-divooka-4fb7&quot;&gt;procedural context&lt;/a&gt;, and other experimental work. Related to this is the improved Neo serialization format, which is becoming more robust and stable. &lt;strong&gt;&lt;em&gt;We haven't yet worked on package management.&lt;/em&gt;&lt;/strong&gt; Although we are trying to make it DiDS-conformant, it's still far from achieving a single final format.&lt;/p&gt;
&lt;p&gt;DSL-wise, there has been initial work on &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/Frameworks/Oista&quot;&gt;Ol'ista&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/Frameworks/Glaze&quot;&gt;Glaze!&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/Frameworks/SlidePresent&quot;&gt;Slide Present&lt;/a&gt;, and &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/Frameworks/Novella&quot;&gt;Novella&lt;/a&gt;. Many utilities were also created for ChatGPT and GenAI services, including interfaces for &lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/oyzLJae3foY?si=NBGaeh1hDTrYsiCZ&quot;&gt;Nvidia NIM&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/V9HC-RErzkY?si=xTcgxoMYH7mjz6_S&quot;&gt;ComfyUI&lt;/a&gt;, and Hugging Face inference services. Different users have different demands, and &lt;strong&gt;it's hard to focus on any specific library without a pilot project&lt;/strong&gt;. While we do have internal pilot projects, they are large in scope and still require groundwork, so Divooka hasn't yet played a significant role. &lt;em&gt;I do want to seek external collaboration on this - but at the moment resources are constrained.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;On the plotting front, as of early August, Aaron has made significant progress on the &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/Divooka/Topics/Plotting&quot;&gt;mid-level plotting API&lt;/a&gt;, making Divooka's plotting library almost &quot;self-sustainable,&quot; able to meet a wide range of demands.&lt;/p&gt;
&lt;p&gt;Although data analytics is arguably not our only focus, use case investigations have improved in-memory DB handling, refactoring, and preliminary libraries for PostgreSQL ingestion. One architectural challenge is expression interpretation: &lt;strong&gt;we decided long ago that, at least for data analytics, it's not efficient to rely entirely on node interfaces - some coding is desirable&lt;/strong&gt;. The main focus is SQL snippets through our in-memory DB library, though at times LINQ expressions and in-place C# scripting have been useful. However, concerns with the latter are robustness, error tolerance, predictability, increased distribution size, and future program export compatibility.&lt;/p&gt;
&lt;p&gt;The data analytics API also faces the challenge of whether to focus on OOP or tabular data as intermediate formats. Sometimes one is more convenient than the other, which increases development costs to maintain both. Eventually, we will likely keep both but prefer exposing the tabular format in Divooka.&lt;/p&gt;
&lt;p&gt;A more complete picture of &quot;data analytics&quot; in Divooka would extend into data science and machine learning. There's plenty of research to do here, &lt;strong&gt;including choosing a foundational library, gradually building our own infrastructure, and developing graph-friendly, visual-programming-first APIs&lt;/strong&gt;. This will have to wait until next year.&lt;/p&gt;
&lt;h3 id=&quot;summary-1&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;The most visible outcome is that Neo is receiving significant updates. On the programming language front, Divooka's function libraries are growing as the language becomes more capable, flexible, and production-ready. Once we start working on package management, it will be more prepared for practical day-to-day use. However, package management requires a fully functional online service first.&lt;/p&gt;
&lt;p&gt;Meanwhile, we are actively exploring more &lt;em&gt;&quot;direct-user-facing&quot;&lt;/em&gt; applications, which I'll discuss in future posts.&lt;/p&gt;
&lt;p&gt;We could use a lot of help on DSL work, and that's the plan as the foundations solidify and architectural decisions become clearer.&lt;/p&gt;
&lt;h3 id=&quot;references-2&quot;&gt;References&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Wiki Entries on related subjects:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/Frameworks/Glaze&quot;&gt;Glaze!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/Frameworks/Novella&quot;&gt;Novella&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/Frameworks/Oista&quot;&gt;Ol'ista&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/Frameworks/SlidePresent&quot;&gt;Slide Present&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;DevLogs on Divooka/Neo progress:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-2025801-procedural-context-type-338k&quot;&gt;DevLog 2025801: Procedural Context Type&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250711-flappy-bird-in-divooka-sneak-peak-40j8&quot;&gt;DevLog 20250711: Flappy Bird in Divooka! (Sneak Peak)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250710-generics-in-divooka-49e6&quot;&gt;DevLog 20250710: Generics in Divooka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250708-procedural-context-visual-debugger-in-divooka-4fb7&quot;&gt;DevLog 20250708: Procedural Context Visual Debugger in Divooka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250619-modularization-improvements-48bi&quot;&gt;DevLog 20250619: Modularization Improvements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250612-olista-web-framework-16mf&quot;&gt;DevLog 20250613: Ol'ista Web Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250610-plotting-in-divooka-7f8&quot;&gt;DevLog 20250610: Plotting in Divooka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250510-dealing-with-lambda-3ff9&quot;&gt;DevLog 20250510 Dealing with Lambda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/progress-share-graph-local-lambda-calculus-4dj2&quot;&gt;Progress Share 20250509: Graph Local Lambda Calculus in Divooka&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The Introductory to Visual Programming with Divooka Textbook:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Access it &lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox-technologies.github.io/TextbookIntroductoryVisualProgramming/&quot;&gt;online here&lt;/a&gt;!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Additional references:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250805-divooka-node-graph-file-formats-19p8&quot;&gt;DevLog 20250805: Divooka Node Graph File Formats&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Releases/Roadmap&quot;&gt;Public Roadmap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/1TuS7ChBb98?si=wmVuuUglw0yljMYQ&quot;&gt;State of Divooka&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;glossary&quot;&gt;Glossary&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;DSL: Domain-specific Library&lt;/li&gt;
&lt;li&gt;DiDS: Divooka Document Specification&lt;/li&gt;
&lt;li&gt;DiOS: Divooka Open Standards&lt;/li&gt;
&lt;li&gt;Gospel: The new, revived, modern incarnation of Parcel, gateway into Divooka; Now a new graph editor frontend for Divooka&lt;/li&gt;
&lt;li&gt;Neo: The Windows graph editor for Divooka&lt;/li&gt;
&lt;li&gt;Morpheus: The cross-platform GUI framework and graph editor architecture for Divooka&lt;/li&gt;
&lt;li&gt;Quadrinity: A way to perceive office suite software as a single framework consisting of multiple views, like Blender's workspaces&lt;/li&gt;
&lt;li&gt;ZFA: Zora Frontier Architecture&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Divooka</category>
<guid isPermaLink="true">https://coderlegion.com/4760/article-divooka-where-we-are</guid>
<pubDate>Sat, 16 Aug 2025 19:42:13 +0000</pubDate>
</item>
<item>
<title>DevLog 20250816: Divooka Language Specification (Preliminary Look)</title>
<link>https://coderlegion.com/4757/devlog-20250816-divooka-language-specification-preliminary-look</link>
<description>&lt;h3 id=&quot;overview-4&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Divooka&lt;/strong&gt; is a cutting-edge visual programming platform developed by Methodox Technologies, Inc. It enables users to build and deploy complex applications through a drag-and-drop, node-based interface that integrates seamlessly with C# and Python libraries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Features&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;General-Purpose &amp;amp; Flexible&lt;/strong&gt;: Suitable for a wide range of use cases - from business tools to innovative software products - supporting both automation and application development.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Node-Based Visual Interface&lt;/strong&gt;: Workflows are constructed visually by connecting nodes that represent data operations, logic, APIs, and more.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multiple Editions&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Divooka Explore&lt;/strong&gt;: A beginner-friendly, Windows-only edition designed for learning, data analytics, dashboards, programming, and everyday utilities.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Divooka Compute&lt;/strong&gt;: A professional package built on the same engine, aimed at power users. Currently available as a Windows preview build, with Linux and macOS support planned for the full release.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-Platform Vision&lt;/strong&gt;: While early versions support Windows, full Linux and macOS support is planned.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strong Architectural Foundations&lt;/strong&gt;: Based on Data-Driven Design principles, Divooka emphasizes modular, external control of behavior through data files - streamlining workflows without modifying code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Developer-Centric Philosophy&lt;/strong&gt;: Divooka is not a new programming language per se. Instead, it focuses on decoupling functionality from specific environments to maximize flexibility and usability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Active Development &amp;amp; Community&lt;/strong&gt;: Ongoing updates, documentation (wiki), tutorials, a Discord community, and blog posts ensure an active ecosystem.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Divooka is built around &lt;strong&gt;node graphs as executable documents&lt;/strong&gt;. Instead of writing sequential code, developers construct graphs of nodes, where each node represents a unit of computation or data. This graph-based approach supports both &lt;strong&gt;dataflow-oriented&lt;/strong&gt; and &lt;strong&gt;procedural-oriented&lt;/strong&gt; paradigms. A Divooka script file (a &quot;Divooka Document&quot;) acts as a container for node graphs.&lt;/p&gt;
&lt;p&gt;At its simplest:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A Divooka document contains multiple graphs.&lt;/li&gt;
&lt;li&gt;Each graph contains multiple nodes.&lt;/li&gt;
&lt;li&gt;Nodes have a type, an optional ID, and attributes.&lt;/li&gt;
&lt;li&gt;Node attributes can connect to other nodes’ attributes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In a &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Divooka/Language/DataflowContext&quot;&gt;Dataflow Context&lt;/a&gt;, node connections are acyclic; in a &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Divooka/Language/ProceduralContext&quot;&gt;Procedural Context&lt;/a&gt;, connections may be cyclic and more flexible.&lt;/p&gt;
&lt;h3 id=&quot;interpretation&quot;&gt;Interpretation&lt;/h3&gt;
&lt;p&gt;To illustrate the simplicity of the language, we can write a minimal interpreter in &lt;strong&gt;Python&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=12773764991494639793&quot; alt=&quot;Diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;This interpreter handles an acyclic graph of nodes with &lt;code&gt;Type&lt;/code&gt;, &lt;code&gt;ID&lt;/code&gt;, attributes (all strings), and connections between attributes. Connections are represented directly as attribute values: if an attribute value starts with &lt;code&gt;@&lt;/code&gt;, it refers to another node’s attribute (e.g., &lt;code&gt;@Node1.Value&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DefineNumber&lt;/code&gt; outputs a number in its &lt;code&gt;Value&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AddNumbers&lt;/code&gt; takes two numbers as inputs and produces a &lt;code&gt;Result&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Print&lt;/code&gt; consumes the &lt;code&gt;Result&lt;/code&gt; and prints it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The interpreter maps node types to operators, executes them, and produces results.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# minimal_graph_interpreter.py
# A tiny, in-memory, non-cyclic node graph + interpreter.
# Nodes have: Type, ID, attrs (all strings). Connections are '@NodeID.Attr'.

from typing import Dict, Any, List, Callable, Optional, Tuple

Node = Dict[str, Any]  # {&quot;ID&quot;: str, &quot;Type&quot;: str, &quot;attrs&quot;: {str:str}, &quot;state&quot;: {str:Any}}

def is_ref(value: Any) -&amp;gt; bool:
    return isinstance(value, str) and value.startswith(&quot;@&quot;) and &quot;.&quot; in value[1:]

def parse_ref(ref: str) -&amp;gt; Tuple[str, str]:
    # &quot;@NodeID.Attr&quot; -&amp;gt; (&quot;NodeID&quot;, &quot;Attr&quot;)
    target = ref[1:]
    node_id, attr = target.split(&quot;.&quot;, 1)
    return node_id, attr

def to_number(s: Any) -&amp;gt; Optional[float]:
    if isinstance(s, (int, float)):
        return float(s)
    if not isinstance(s, str):
        return None
    try:
        return float(int(s))
    except ValueError:
        try:
            return float(s)
        except ValueError:
            return None

class Interpreter:
    def __init__(self, nodes: List[Node]):
        # normalize nodes and build index
        self.nodes: List[Node] = []
        self.by_id: Dict[str, Node] = {}
        for n in nodes:
            node = {&quot;ID&quot;: n[&quot;ID&quot;], &quot;Type&quot;: n[&quot;Type&quot;], &quot;attrs&quot;: dict(n.get(&quot;attrs&quot;, {})), &quot;state&quot;: {}}
            self.nodes.append(node)
            self.by_id[node[&quot;ID&quot;]] = node

        # map Type -&amp;gt; evaluator
        self.ops: Dict[str, Callable[[Node], bool]] = {
            &quot;DefineNumber&quot;: self.op_define_number,
            &quot;AddNumbers&quot;: self.op_add_numbers,
            &quot;Print&quot;: self.op_print,
        }

    # ---- helpers ----
    def get_attr_value(self, node_id: str, attr: str) -&amp;gt; Any:
        &quot;&quot;&quot;Return the most 'evaluated' value for an attribute (state overrides attrs).&quot;&quot;&quot;
        node = self.by_id.get(node_id)
        if not node:
            return None
        if attr in node[&quot;state&quot;]:
            return node[&quot;state&quot;][attr]
        return node[&quot;attrs&quot;].get(attr)

    def resolve(self, raw: Any) -&amp;gt; Any:
        &quot;&quot;&quot;Dereference '@Node.Attr' chains once (graph is acyclic so one hop is enough).&quot;&quot;&quot;
        if is_ref(raw):
            nid, a = parse_ref(raw)
            return self.get_attr_value(nid, a)
        return raw

    def all_resolved(self, values: List[Any]) -&amp;gt; bool:
        return all(not is_ref(v) and v is not None for v in values)

    # ---- operators ----
    def op_define_number(self, node: Node) -&amp;gt; bool:
        # Input: attrs[&quot;Value&quot;] (string number). Output: state[&quot;Value&quot;] (numeric)
        if &quot;Value&quot; in node[&quot;state&quot;]:
            return False  # already done
        raw = node[&quot;attrs&quot;].get(&quot;Value&quot;)
        val = self.resolve(raw)
        num = to_number(val)
        if num is None:
            return False  # can't parse yet
        node[&quot;state&quot;][&quot;Value&quot;] = num
        return True

    def op_add_numbers(self, node: Node) -&amp;gt; bool:
        # Inputs: attrs[&quot;Value1&quot;], attrs[&quot;Value2&quot;] (can be @ refs). Output: state[&quot;Result&quot;]
        if &quot;Result&quot; in node[&quot;state&quot;]:
            return False
        v1 = to_number(self.resolve(node[&quot;attrs&quot;].get(&quot;Value1&quot;)))
        v2 = to_number(self.resolve(node[&quot;attrs&quot;].get(&quot;Value2&quot;)))
        if v1 is None or v2 is None:
            return False
        node[&quot;state&quot;][&quot;Result&quot;] = v1 + v2
        return True

    def op_print(self, node: Node) -&amp;gt; bool:
        # Input: attrs[&quot;Result&quot;] (@ ref). Side effect: print once. Also store state[&quot;Printed&quot;]=True
        if node[&quot;state&quot;].get(&quot;Printed&quot;):
            return False
        r = self.resolve(node[&quot;attrs&quot;].get(&quot;Result&quot;))
        # Allow printing numbers or strings once the reference resolves
        if r is None or is_ref(r):
            return False
        print(r)
        node[&quot;state&quot;][&quot;Printed&quot;] = True
        return True

    # ---- execution ----
    def step(self) -&amp;gt; bool:
        &quot;&quot;&quot;Try to make progress by evaluating any node whose inputs are ready.&quot;&quot;&quot;
        progressed = False
        for node in self.nodes:
            op = self.ops.get(node[&quot;Type&quot;])
            if not op:
                # Unknown node type: ignore
                continue
            progressed = op(node) or progressed
        return progressed

    def run(self, max_iters: int = 100):
        &quot;&quot;&quot;Iteratively evaluate until no changes (DAG assumed, so this stabilizes quickly).&quot;&quot;&quot;
        for _ in range(max_iters):
            if not self.step():
                return
        raise RuntimeError(&quot;Exceeded max iterations (graph might be cyclic or ill-formed).&quot;)


if __name__ == &quot;__main__&quot;:
    # --- Example in-memory graph ---
    graph = [
        {&quot;ID&quot;: &quot;Node1&quot;, &quot;Type&quot;: &quot;DefineNumber&quot;, &quot;attrs&quot;: {&quot;Value&quot;: &quot;3&quot;}},
        {&quot;ID&quot;: &quot;Node2&quot;, &quot;Type&quot;: &quot;DefineNumber&quot;, &quot;attrs&quot;: {&quot;Value&quot;: &quot;5&quot;}},
        {
            &quot;ID&quot;: &quot;Adder&quot;,
            &quot;Type&quot;: &quot;AddNumbers&quot;,
            &quot;attrs&quot;: {&quot;Value1&quot;: &quot;@Node1.Value&quot;, &quot;Value2&quot;: &quot;@Node2.Value&quot;},
        },
        {&quot;ID&quot;: &quot;Printer&quot;, &quot;Type&quot;: &quot;Print&quot;, &quot;attrs&quot;: {&quot;Result&quot;: &quot;@Adder.Result&quot;}},
    ]

    interp = Interpreter(graph)
    interp.run()   # Should print: 8.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running the example graph prints:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;8.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;summary-2&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;The Divooka language demonstrates how a &lt;strong&gt;minimalist graph-based specification&lt;/strong&gt; can serve as a foundation for both &lt;strong&gt;computation and orchestration&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Key takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Node-Centric Abstraction&lt;/strong&gt;: Everything is reduced to nodes with types, IDs, and attributes - uniform, extensible, and easy to interpret.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simple Reference Mechanism&lt;/strong&gt;: The &lt;code&gt;@NodeID.Attr&lt;/code&gt; convention provides a straightforward but powerful way to connect attributes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separation of Concerns&lt;/strong&gt;: Distinguishing between &lt;em&gt;dataflow&lt;/em&gt; (acyclic, deterministic) and &lt;em&gt;procedural&lt;/em&gt; (control flow, cyclic) contexts allows Divooka to cover both declarative and imperative styles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Composable Operators&lt;/strong&gt;: Even with just three operators (&lt;code&gt;DefineNumber&lt;/code&gt;, &lt;code&gt;AddNumbers&lt;/code&gt;, &lt;code&gt;Print&lt;/code&gt;), meaningful behaviors emerge.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compact Interpreter Footprint&lt;/strong&gt;: The entire interpreter is under 200 lines of Python, demonstrating the specification’s simplicity and rapid prototyping potential.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One might ask why not use traditional graph connections. The answer is simplicity: defining connections as local attribute references reduces structure while keeping graphs clean. In dataflow, inputs typically come from a single source, while in procedural contexts, outputs are unique but inputs may be shared, so we can just reverse the syntax - making this lightweight approach intuitive and efficient.&lt;/p&gt;
&lt;h3 id=&quot;reference&quot;&gt;Reference&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Wiki (WIP): &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Standardization/DiLS&quot;&gt;Divooka Language Specification&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Divooka</category>
<guid isPermaLink="true">https://coderlegion.com/4757/devlog-20250816-divooka-language-specification-preliminary-look</guid>
<pubDate>Sat, 16 Aug 2025 19:41:04 +0000</pubDate>
</item>
<item>
<title>State of Divooka | 2025 Vancouver Web Summit | Video Demo | Time-lapse</title>
<link>https://coderlegion.com/4492/state-of-divooka-2025-vancouver-web-summit-video-demo-time-lapse</link>
<description>&lt;p&gt;Divooka is a powerful general-purpose visual programming system that brings a new level of flexibility to general-purpose software development. Its intuitive node-based interface allows you to design, automate, and deploy complex applications with ease, connecting C# and Python libraries seamlessly. Whether you're building robust business solutions or innovative software products, Divooka offers a cross-platform environment that accelerates development and enhances collaboration. Discover a new way to code—visually, intuitively, and efficiently.&lt;/p&gt;
&lt;p&gt;In this video demo, we walk through some of the features available as of early 2025.&lt;/p&gt;
&lt;p&gt;Topics:&lt;/p&gt;
&lt;p&gt;00:00 Library Survey&lt;br&gt;
00:01 Audio Operations&lt;br&gt;
00:08 Make Presentation&lt;br&gt;
00:32 Build Interactive Media&lt;br&gt;
00:51 Integrate Generative AI&lt;br&gt;
01:04 Number Operations&lt;br&gt;
01:11 String Operations&lt;br&gt;
01:28 ComfyUI API&lt;br&gt;
01:38 Image Processing&lt;br&gt;
01:55 Generate QR Code&lt;/p&gt;
&lt;p&gt;02:05 Use Case&lt;br&gt;
02:06 Data Analytics&lt;br&gt;
02:25 Generate Entire Book with LLM&lt;/p&gt;
&lt;p&gt;Website: &lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox.io/&quot;&gt;Methodox&lt;/a&gt;&lt;br&gt;
Store: &lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox.itch.io/&quot;&gt;Methodox Itch.io&lt;/a&gt;&lt;br&gt;
Release announcement: &lt;a rel=&quot;nofollow&quot; href=&quot;https://itch.io/t/4911014/methodox-divooka-general-purpose-visual-programming-language&quot;&gt;Itch.io&lt;/a&gt;&lt;br&gt;
Official wiki: &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/&quot;&gt;wiki&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Also see article: &lt;a rel=&quot;nofollow&quot; href=&quot;https://medium.methodox.io/divooka-where-we-are-or-public-roadmap-5e13b86e6e46&quot;&gt;Divooka, Where We Are&lt;/a&gt;&lt;/p&gt;
</description>
<category>Programming Lang</category>
<guid isPermaLink="true">https://coderlegion.com/4492/state-of-divooka-2025-vancouver-web-summit-video-demo-time-lapse</guid>
<pubDate>Thu, 07 Aug 2025 17:26:32 +0000</pubDate>
</item>
<item>
<title>DevLog 20250805: Divooka Node Graph File Formats</title>
<link>https://coderlegion.com/4452/devlog-20250805-divooka-node-graph-file-formats</link>
<description>&lt;h3 id=&quot;preamble&quot;&gt;Preamble&lt;/h3&gt;
&lt;p&gt;When I first envisioned Divooka, I aimed for open source, open file formats, and strong version control friendliness (especially with Git). JSON is verbose and ugly, YAML is perfect but too complex, and formats like CSV/RTS/OBJ are simple and elegant but lack the flexibility needed for complex structures. A custom line-based format seemed like a good middle ground and necessary starting point.&lt;/p&gt;
&lt;p&gt;This led to the intention of creating a simple, human-friendly, and easy-to-parse file format - something like TOML. This approach has proven effective in the past; I've parsed similar formats before, and it works well for INI, config, and even Godot scene files.&lt;/p&gt;
&lt;h4 id=&quot;canonical-toml-example&quot;&gt;Canonical TOML Example&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;# This is a TOML document

title = &quot;TOML Example&quot;

[owner]
name = &quot;Tom Preston-Werner&quot;
dob = 1979-05-27T07:32:00-08:00

[database]
enabled = true
ports = [ 8000, 8001, 8002 ]
data = [ [&quot;delta&quot;, &quot;phi&quot;], [3.14] ]
temp_targets = { cpu = 79.5, case = 72.0 }

[servers]

[servers.alpha]
ip = &quot;10.0.0.1&quot;
role = &quot;frontend&quot;

[servers.beta]
ip = &quot;10.0.0.2&quot;
role = &quot;backend&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The only issue I have with TOML is largely aesthetic: it's just not beautiful enough. Objects are too scattered, and it can feel a bit verbose - especially when incorporating type information, as seen in a Godot scene file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-toml&quot;&gt;[gd_scene load_steps=2 format=3 uid=&quot;uid://dbmf10vsl4g6q&quot;]

[ext_resource type=&quot;Script&quot; uid=&quot;uid://dcyr50rclg8m1&quot; path=&quot;res://scenes/login.gd&quot; id=&quot;1_l7r8x&quot;]

[node name=&quot;Login&quot; type=&quot;Control&quot;]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource(&quot;1_l7r8x&quot;)

[node name=&quot;VBoxContainer&quot; type=&quot;VBoxContainer&quot; parent=&quot;.&quot;]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -80.0
offset_top = -80.5
offset_right = 80.0
offset_bottom = 80.5
grow_horizontal = 2
grow_vertical = 2

[node name=&quot;Label&quot; type=&quot;Label&quot; parent=&quot;VBoxContainer&quot;]
layout_mode = 2
theme_override_font_sizes/font_size = 46
text = &quot;Login&quot;
horizontal_alignment = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-two-approaches&quot;&gt;The Two Approaches&lt;/h3&gt;
&lt;h4 id=&quot;divooka-open-standard-plain-text-binary-hybrid-&quot;&gt;Divooka Open Standard (Plain Text/Binary Hybrid)&lt;/h4&gt;
&lt;p&gt;We’ll eventually discuss the broader &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Standardization&quot;&gt;Divooka Open Standard&lt;/a&gt;, but for now, let’s focus specifically on file formats. My original vision was for something like Blender’s &lt;code&gt;.blend&lt;/code&gt; format - except in plain text: a sequence of self-contained data blocks that allow easy extension and are powerful enough to store arbitrary payloads.&lt;/p&gt;
&lt;p&gt;I imagined blocks for: document properties, nodes, graphs (including connections), payloads, revisions, etc. Some attributes - such as node positions and sizes - are considered auxiliary and handled through &quot;decorators&quot; rather than being part of the core schema. Perhaps I went too far here: the flexibility results in many small structures holding trivial bits of data. On the bright side, once implemented, this approach yields a format that is both human-readable and programmatically digestible. Interpreters uninterested in positional data, for instance, can simply ignore those decorators.&lt;/p&gt;
&lt;p&gt;One of the early design decisions was to keep all attributes as strings. The idea was to rely on parsing for interpretation and avoid enforcing types directly. Much like the web, this promotes forward compatibility and long-term document stability. I still stand by this: nothing is easier to parse than plain text.&lt;/p&gt;
&lt;p&gt;That said, I made a mistake in implementing everything strictly in this format: &lt;strong&gt;the string-based format only makes sense during (de)serialization. Once in memory, the data should be converted into strongly typed structures before any meaningful work is done.&lt;/strong&gt; Otherwise, everything becomes cluttered with unnecessary boilerplate.&lt;/p&gt;
&lt;h4 id=&quot;pv1-neo-document-format-binary-&quot;&gt;PV1 Neo Document Format (Binary)&lt;/h4&gt;
&lt;p&gt;PV1 Neo takes a more opinionated, object-oriented approach. Documents contain graphs, which in turn contain nodes and connections. Each of these is represented as a strongly typed object. By default, nodes include layout properties like position and size.&lt;/p&gt;
&lt;p&gt;This results in a neat OOP hierarchy where everything is properly defined and serialized. The upside is ease of development - you simply call &lt;code&gt;Serialize()&lt;/code&gt; at the top level and the rest follows. However, the downside is that parsing becomes nearly impossible without full knowledge of the exact object types. Any misalignment in type expectations results in binary stream reading errors.&lt;/p&gt;
&lt;h3 id=&quot;derivations&quot;&gt;Derivations&lt;/h3&gt;
&lt;p&gt;Since Divooka graphs can already be represented in text via the Open Standard, I’ve proposed two derived, minimalist formats: &lt;strong&gt;MiniParcel&lt;/strong&gt; and &lt;strong&gt;MicroParcel&lt;/strong&gt;. These are lightweight, CLI-friendly formats designed for shell use. The rationale is that since nodes can represent abstract functions or endpoints, users should be able to manipulate them without relying on a GUI.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Default:
    FileList = GetFiles &quot;{tempFolderPath}&quot;
    FileRenameList = Enhance @FileList !#Rename
    Apply @FileRenameList !MoveFile
Rename:
    FileName = GetOnlyName (GetArgument :0)
    Return (Replace @FileName &quot;:File_Sequence_00(.)&quot; &quot;:File-Sequence-00$1&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;conclusion-5&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;As we finalize prototyping and start committing to core design decisions, file format is one area requiring careful long-term thinking. There’s an inherent trade-off between programmability and readability/portability - especially since Divooka documents may eventually hold a vast variety of data.&lt;/p&gt;
&lt;p&gt;In the foreseeable future, both formats may coexist. In fact, it might make sense to support both for the long term. As Divooka matures and gains better standardization, I’m hopeful we’ll arrive at a robust format that truly supports the long-term vision.&lt;/p&gt;
&lt;h3 id=&quot;references-3&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Rationale for Godot’s custom INI format: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/godotengine/godot/issues/10069&quot;&gt;GitHub discussion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Divooka Document Specification: &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Standardization/FileFormat&quot;&gt;future link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Divooka</category>
<guid isPermaLink="true">https://coderlegion.com/4452/devlog-20250805-divooka-node-graph-file-formats</guid>
<pubDate>Wed, 06 Aug 2025 00:57:32 +0000</pubDate>
</item>
<item>
<title>DevLog 2025801: Procedural Context Type</title>
<link>https://coderlegion.com/4381/devlog-2025801-procedural-context-type</link>
<description>&lt;h3 id=&quot;overview-5&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;In our pursuit to challenge existing visual paradigms and establish the next-generation general-purpose visual programming system, we've studied, borrowed from, and questioned many design elements of existing systems.&lt;/p&gt;
&lt;p&gt;An area of innovation in Divooka is its hybrid use of dataflow and procedural contexts - each suited for particular use cases.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l5wo9dlxp01octrgexw0.png&quot; alt=&quot;Mixed Context&quot;&gt;&lt;/p&gt;
&lt;p&gt;Another innovation, still in its infancy and improving through trial and error, is what we call the &lt;strong&gt;procedural context base type&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;The need for this concept emerged as we integrated procedural contexts and re-examined the pain points of working with advanced visual programming systems like Unreal Blueprint. Changing the base type of Blueprints often leads to disastrous outcomes.&lt;/p&gt;
&lt;p&gt;We aim to provide a smoother experience. In Divooka, the procedural &quot;base type&quot; is a toggle - you can quickly switch a graph to a different base type. Everything that isn't type-dependent continues to work, and any function that is type-dependent simply indicates so.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4b3m2d9plfvxrr23qkdf.png&quot; alt=&quot;Picker&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;components&quot;&gt;Components&lt;/h3&gt;
&lt;p&gt;The procedural base type introduces four important concepts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Procedural base type (current anchor)&lt;/strong&gt;: The current graph's base, representing the instance that functions in the graph are called on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Procedural runner for any given base type&lt;/strong&gt;: When a run is triggered, Divooka allows a custom runner to determine what actually happens. In a typical program, this runner might seek the &quot;entry&quot; function (e.g., &lt;code&gt;Start&lt;/code&gt;) and execute from there. In higher-level frameworks, it might look for recognizable event bindings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Procedural base type instance override&lt;/strong&gt;: By default, the specified base type is instantiated at runtime to provide a concrete instance. However, the runtime can also supply a compatible instance for the graph's behavior to operate on. This allows flexibility and decouples implementation (runtime instance) from behavior specification (graph design).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Divooka graph-baked procedural instance type&lt;/strong&gt;: It may be desirable to directly convert or interpret a graph as a concrete type. The main benefit is being able to pass the graph as a type for further processing. This feature is not yet implemented.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Based on the above, three key objects are involved in evaluating a procedural context graph:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Executer&lt;/strong&gt;: Provides the logic and mechanisms to fetch and evaluate specific execution paths in the procedural graph.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Runner&lt;/strong&gt;: Determines what to run and in what order, on an event level. It may also provide additional runtime data required by the instance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instance&lt;/strong&gt;: Represents the runtime state of the graph.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;summary-3&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;In summary, a Divooka graph is a data structure that specifies behavior, while the runtime interprets and is responsible for actual execution. This makes Divooka a data-driven and highly extensible programming system suitable for various scenarios.&lt;/p&gt;
&lt;p&gt;By combining the scripting nature of Divooka graphs with the compiled/JIT-accelerated nature of the runtime, Divooka offers both flexibility and dynamic richness - while minimizing friction in program development.&lt;/p&gt;
</description>
<category>Divooka</category>
<guid isPermaLink="true">https://coderlegion.com/4381/devlog-2025801-procedural-context-type</guid>
<pubDate>Sat, 02 Aug 2025 01:47:41 +0000</pubDate>
</item>
<item>
<title>DevLog 20250729: Procedural Terrain Generation</title>
<link>https://coderlegion.com/4324/devlog-20250729-procedural-terrain-generation</link>
<description>&lt;h3 id=&quot;intro&quot;&gt;Intro&lt;/h3&gt;
&lt;p&gt;Procedural generation, often called algorithmic art, is the process of turning code into visual content. Unlike generative AI, which can often feel &quot;either works or doesn't&quot;, procedural generation offers predictable results and detailed control over every element of the outcome. Unlike simulation, its process is less about modeling physical phenomena and more about manipulating data through arithmetic and logic.&lt;/p&gt;
&lt;p&gt;In practical production, procedural generation rarely stands alone. It’s usually combined with hand-crafted elements to achieve a balance between efficiency and artistic vision - a good example is the diverse planets in &lt;em&gt;No Man’s Sky&lt;/em&gt;, which mix algorithmic rules with human-made textures and assets. Unreal Engine’s &lt;em&gt;City Demo&lt;/em&gt; is, in my opinion, one of the most condensed showcases of how far this paradigm can be pushed. Yet, as I will discuss in a future article, procedural generation also faces diminishing returns: the more we aim for total automation, the less room we leave for meaningful artistic decisions. For world-building (not just game design), it often makes more sense to focus on curated, high-quality content rather than endlessly expanding algorithmic variety.&lt;/p&gt;
&lt;h3 id=&quot;how-does-it-has-to-do-with-divooka-&quot;&gt;How does it has to do with Divooka?&lt;/h3&gt;
&lt;p&gt;When we talk about general-purpose, low-level procedural generation, three tools come to mind: &lt;strong&gt;Houdini&lt;/strong&gt;, &lt;strong&gt;Blender Geometry Nodes&lt;/strong&gt;, and &lt;strong&gt;Rhino Grasshopper&lt;/strong&gt;. All three embrace a dataflow mindset, where complex results emerge from chaining and manipulating small, modular operations.&lt;/p&gt;
&lt;p&gt;At its core, procedural geometry is about transforming data points - vertices, edges, and faces - through parametric relationships. The same happens when working with a dataflow diagram: each node transforms data in a specific way, and the network of nodes collectively defines the outcome. This data-centric, rule-based mindset has a strong connection to what I envision for &lt;strong&gt;Divooka&lt;/strong&gt;. It’s not just about &quot;automating&quot; art; it’s about building a flexible, modular pipeline where small algorithmic rules can shape a larger creative vision.&lt;/p&gt;
&lt;h3 id=&quot;procedural-terrain-generation&quot;&gt;Procedural Terrain Generation&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u44ynxiwv1dwpyy4mrm3.png&quot; alt=&quot;Fantasy Planet Painter&quot;&gt;&lt;/p&gt;
&lt;p&gt;Among all categories of procedural content, terrain generation stands out as being easily feasible while also present just enough technical and artistic challenge. Procedural terrains, trees, and clouds - when done well - can be indispensable in environmental design. These elements create a sense of scale, variety, and believability that would be prohibitively expensive to craft by hand, especially at scale.&lt;/p&gt;
&lt;p&gt;Procedural humans are another major frontier, as projects like &lt;strong&gt;MetaHuman&lt;/strong&gt; have shown. However, most current solutions in this area remain asset-based (pre-designed components combined procedurally) rather than fully algorithm-driven, due to the complexity of human anatomy and subtle facial variations.&lt;/p&gt;
&lt;p&gt;What makes procedural terrain generation truly interesting is its multi-scale nature. The same algorithms that define the curvature of a hill, the placement of a forest, or the erosion patterns of a mountain range can be scaled up as procedural rules to shape entire planets, as seen in projects like &lt;em&gt;Outerra&lt;/em&gt; or the planet tech behind &lt;em&gt;Star Citizen&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;For terrains specifically, there are multiple approaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Parametric modeling:&lt;/strong&gt; Platforms like &lt;strong&gt;TerraGen&lt;/strong&gt; and &lt;strong&gt;Vue&lt;/strong&gt; provides drop-in procedural terrains that works at large scale, at the sacrifice of simulating natural erosion, weathering, and geology.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Heightmap-based workflows:&lt;/strong&gt; Tools like &lt;strong&gt;World Machine&lt;/strong&gt; remain the gold standard here, providing artists with granular control over erosion layers, noise functions, and texturing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hybrid artistic sculpting:&lt;/strong&gt; Recent software such as &lt;strong&gt;World Creator&lt;/strong&gt; blends procedural systems with direct, real-time sculpting, letting artists guide the procedural logic for more intentional results.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Biomes and planet-scale world maps:&lt;/strong&gt; Beyond local terrain detail, truly massive environments need a global &quot;climate&quot; layer to drive ecosystem placement and continental shapes. Typically you start by generating low-frequency noise on a sphere (using an icosphere or cubemap projection) to define elevation, temperature, and moisture fields. These three channels feed a biome classifier (for example, using Whittaker’s biome chart) to assign regions like tundra, desert, rainforest, or grassland. Then, by layering higher-frequency noise and erosion simulations, you carve coastlines and mountain ranges that respect those climate zones. On top of that, you can stream in progressively finer mesh tiles or voxel chunks as the viewer approaches, blending seamlessly from a planet-wide view down to individual hills and caves. This multi-scale, data-driven pipeline lets you create a consistent, believable world map—where a river born in snowy peaks will reliably flow toward warmer, wetter biomes, and deserts will hug the rain shadows of mountain ridges.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Voxel and simulation-driven worlds:&lt;/strong&gt; Games like &lt;em&gt;Minecraft&lt;/em&gt; and &lt;em&gt;Dwarf Fortress&lt;/em&gt; have shown that procedural generation can build entire, fully interactive worlds at massive scale, with layers of terrain below the surface.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion-6&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Procedural generation is more than a tool - it’s a mindset, and a skill requiring practising. It allows creators to define rules and systems rather than individual assets, creating worlds that feel alive, infinite, and varied. However, its real power comes when balanced with handcrafted content, ensuring that algorithmic variety never comes at the cost of artistic intent.&lt;/p&gt;
&lt;p&gt;For Divooka, the challenge (and opportunity) lies in embracing procedural workflows without losing sight of the content’s purpose. A beautiful world isn’t necessarily one that is infinitely generated - it’s one where the procedural tools empower the creator to focus on meaning, storytelling, and design.&lt;/p&gt;
</description>
<category>Game Dev</category>
<guid isPermaLink="true">https://coderlegion.com/4324/devlog-20250729-procedural-terrain-generation</guid>
<pubDate>Tue, 29 Jul 2025 17:21:12 +0000</pubDate>
</item>
<item>
<title>DevLog 20250728: Multi-graph Navigation GUI</title>
<link>https://coderlegion.com/4322/devlog-20250728-multi-graph-navigation-gui</link>
<description>&lt;h3 id=&quot;overview-6&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;From the beginning, we've expected Divooka documents to support multiple graphs for efficient daily use. I consider this essential for scripting purposes, as dealing with file system entry references should be the last thing on our minds when trying to get something done quickly.&lt;/p&gt;
&lt;p&gt;However, implementing this isn't straightforward. Several interesting and foundational features are required first: serialization, file format support, editor refactoring - and graph referencing, which we may discuss in a future entry.&lt;/p&gt;
&lt;p&gt;In traditional programming languages, especially object-oriented ones, similar functionality is often achieved by allowing multiple classes per file. In C#, for example, it's even possible to define partial classes across separate files, which can become confusing if not managed carefully.&lt;/p&gt;
&lt;h3 id=&quot;objective&quot;&gt;Objective&lt;/h3&gt;
&lt;p&gt;When working with multiple graphs in a single document, the experience should feel similar to working with separate documents that each contain a single graph. At the most basic level, the editor should allow quick and seamless switching between graphs. More advanced features will come later. For example, storing multiple graphs within a single document should eventually enable referencing those graphs or documents from other documents.&lt;/p&gt;
&lt;h3 id=&quot;implementation-progress&quot;&gt;Implementation Progress&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k3gcza59pu9l1lpes82s.png&quot; alt=&quot;Graphs Selection&quot;&gt;&lt;/p&gt;
&lt;p&gt;Today's goal was to support navigation and switching between different graphs in Neo.&lt;/p&gt;
&lt;p&gt;Preliminary work on refactoring the graph editor into a reusable component was already completed earlier. This refactor is also crucial for enabling the subgraph editing window.&lt;/p&gt;
&lt;p&gt;To maintain context when switching graphs, the current viewport remains unchanged. We've also implemented a mini-map to help users quickly identify their location within the graph.&lt;/p&gt;
&lt;h3 id=&quot;minimap-behavior&quot;&gt;Minimap Behavior&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/unxovuu86yg2srk4tjd0.png&quot; alt=&quot;Minimap&quot;&gt;&lt;/p&gt;
&lt;p&gt;The math behind the minimap is a bit tricky, so it's worth documenting:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When switching between graphs, the editor should either preserve the current viewport (location and zoom) or reset the view to the center of the new graph.&lt;/li&gt;
&lt;li&gt;A &quot;world boundary&quot; must be calculated that encompasses all nodes in the graph.&lt;/li&gt;
&lt;li&gt;A &quot;viewport boundary&quot; should also be calculated to represent the currently visible area of the canvas.&lt;/li&gt;
&lt;li&gt;The minimap scale (typically less than 1 to represent downscaling) should be the minimum of:  &lt;ul&gt;
&lt;li&gt;&lt;code&gt;minimap size / world size&lt;/code&gt;, or  &lt;/li&gt;
&lt;li&gt;&lt;code&gt;minimap size / viewport size&lt;/code&gt;, if the viewport is larger than the world (e.g., when zoomed out).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;other-matters&quot;&gt;Other Matters&lt;/h3&gt;
&lt;p&gt;The Neo graph editor is taking noticeably longer to load lately. We may need to investigate and address performance issues soon.&lt;/p&gt;
</description>
<category>Divooka</category>
<guid isPermaLink="true">https://coderlegion.com/4322/devlog-20250728-multi-graph-navigation-gui</guid>
<pubDate>Tue, 29 Jul 2025 17:19:46 +0000</pubDate>
</item>
<item>
<title>DevLog 20250726: C# is the way, Python gets things done, JavaScript is convenient</title>
<link>https://coderlegion.com/4280/devlog-20250726-c-is-the-way-python-gets-things-done-javascript-is-convenient</link>
<description>&lt;p&gt;Throughout my day-to-day life, both personal and professional, there is a need for lots of &quot;small&quot; utilities, like converting a TSV to a Markdown table, generating a random choice, converting PNG to ICO, or quickly counting the number of lines in a C# project.&lt;/p&gt;
&lt;p&gt;In the past, I've addressed all of those with a mix of Pure and C# - most are available in source. However, lately I've been hitting efficiency bottlenecks even with Pure or Divooka.&lt;/p&gt;
&lt;p&gt;Today I was once again trying to convert a TSV to Markdown. However, since I haven't done it for a while, even though I know the script must exist somewhere on my disk, I do not want to waste time looking for it using Everything. Instead, what I really want is to be able to instantaneously spawn it and just run it. We could set up shortcuts, and maybe even make use of Launcher (Big White Dot) - but it would be really nice if there were a single entry point to search for such tools, and it's always available, no matter where I am, or which device I am using - like a random choice generator.&lt;/p&gt;
&lt;p&gt;Apparently, for such simple small tasks, like random choice generation, Markdown conversion, Markdown table conversion, and PNG/ICO conversion, there are already tons of similar websites for quick, easy online use. However, most of them are filled with ads or have unclear privacy terms. It's thus more desirable to have a host of my personally built, personally customized, clean utilities, completely under my control.&lt;/p&gt;
&lt;p&gt;Nowadays, with video coding and advanced code generators, I can create a single-page website in a few minutes, push it to GitHub, and a few minutes later the utility is accessible online - no ads, no hosting, no additional cost. That code is in HTML + CSS + JavaScript, though.&lt;/p&gt;
&lt;p&gt;A similar thing happened a few weeks back for a personal project in which I had invested heavily in C++ before. During a revisit, I realized I could easily achieve the same results, plus more, with Python and some third-party libraries. I won't consider that setup &quot;production grade,&quot; but it's stable, safe, and can likely run for years to come.&lt;/p&gt;
&lt;p&gt;We always say &quot;use the right tool for the task&quot; - but we can be conservative by asking questions like: what if I need to link this in other apps, what if I need to reuse it, what if the code grows and I need more advanced debugging support? But eventually, the right approach is just to start over in a new language or setup when the time comes. I know C# is the way, but Python can get things done, and JavaScript is just convenient for immediately making things accessible.&lt;/p&gt;
&lt;p&gt;What does this mean for Divooka? Well, convenience has many facets, and I think we should focus on accessibility first.&lt;/p&gt;
</description>
<category>Divooka</category>
<guid isPermaLink="true">https://coderlegion.com/4280/devlog-20250726-c-is-the-way-python-gets-things-done-javascript-is-convenient</guid>
<pubDate>Sat, 26 Jul 2025 19:57:25 +0000</pubDate>
</item>
<item>
<title>DevLog 20250724: C# Scripting, Pure 2, Updated + Some Recap</title>
<link>https://coderlegion.com/4264/devlog-20250724-c-scripting-pure-2-updated-some-recap</link>
<description>&lt;h3 id=&quot;intro-1&quot;&gt;Intro&lt;/h3&gt;
&lt;p&gt;Pure has been our in-house C# scripting solution for a few years now.&lt;br&gt;
After experimenting with the well-known &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/CS-Script&quot;&gt;CS-Script&lt;/a&gt; and some other solutions I can no longer recall, I began experimenting with &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Roslyn_(compiler)&quot;&gt;Roslyn&lt;/a&gt; in .NET around 2022/2023, and it's been working well ever since.&lt;/p&gt;
&lt;h4 id=&quot;usage&quot;&gt;Usage&lt;/h4&gt;
&lt;p&gt;Below is a basic Pure script and its execution:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;using static System.Console;

var sum = File.ReadLines(&quot;Data.csv&quot;)
    .Skip(1)
    .Select(l =&amp;gt; l.Split(','))
    .Sum(parts =&amp;gt; double.Parse(parts[1]));
WriteLine(sum);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://publication.tor1.cdn.digitaloceanspaces.com/Websites/MethodoxWiki/Images/Utilities-CLI-Pure-Basic_Example.png&quot; alt=&quot;Result&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;recap&quot;&gt;Recap&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Version 0.0.3&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1g0yfd5tu0wiq9natttt.png&quot; alt=&quot;v0.0.3&quot;&gt;&lt;/p&gt;
&lt;p&gt;The first version of Pure was just a simple wrapper around Roslyn. Later, we expanded it to support syntax like &lt;code&gt;Import(MathNet.Numerics)&lt;/code&gt;. It wasn't until we added native libraries like &lt;code&gt;CSV&lt;/code&gt;, &lt;code&gt;ODBC&lt;/code&gt;, and &lt;code&gt;CentralSnippets&lt;/code&gt; that things started to get interesting.&lt;/p&gt;
&lt;h3 id=&quot;motivation&quot;&gt;Motivation&lt;/h3&gt;
&lt;p&gt;Below is an old table I compiled when first drafting the design document for Pure (1), with some updates to mention newer solutions:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=292194321662759487&quot; alt=&quot;Table 1&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-s-special&quot;&gt;What's Special&lt;/h3&gt;
&lt;p&gt;Pure 2 is a lightweight C# scripting platform with modern C# 12 syntax and easy NuGet import support. Pure is based on Roslyn. In addition to the base .NET runtime, Pure offers additional libraries for handling common tasks, along with a handy scripting interface (REPL and Notebook) and a place where people can share their &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Pure-the-Language/CentralSnippets&quot;&gt;snippets&lt;/a&gt;.  &lt;/p&gt;
&lt;p&gt;Pure 2 is conceptually similar to &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Pure-the-Language/Pure&quot;&gt;Pure&lt;/a&gt; but architecturally very different - it directly builds upon Parcel NExT's Ama runtime (the same runtime used by Divooka). Pure is designed for very short snippets and getting everyday tasks done faster.&lt;/p&gt;
&lt;p&gt;Pure 2 uses C# 12 and is similar to &lt;a rel=&quot;nofollow&quot; href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/top-level-statements&quot;&gt;top-level statements&lt;/a&gt; in C#, with the following features:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You can define types and functions anywhere in the middle of the script.&lt;/li&gt;
&lt;li&gt;Pure has its own package import behavior (default namespace usage and package lookup rules).&lt;/li&gt;
&lt;li&gt;Default global scope math functions, and default &lt;code&gt;using&lt;/code&gt; for &lt;code&gt;System.IO&lt;/code&gt; and &lt;code&gt;System.Linq&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Top-level function and variable definitions, alongside classes and other common C# constructs.&lt;/li&gt;
&lt;li&gt;Simplifies common tasks even further.&lt;/li&gt;
&lt;li&gt;Macros to support text-based parsing, file inclusion, and module imports.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;features&quot;&gt;Features&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Single-word standard package names. One-command NuGet package import and on-the-fly package invocation (through &lt;code&gt;Import&lt;/code&gt;).  &lt;/li&gt;
&lt;li&gt;Full compatibility with Divooka packages and Divooka graphs.&lt;/li&gt;
&lt;li&gt;Pure uses &lt;code&gt;PUREPATH&lt;/code&gt; to search for scripts when using &lt;code&gt;Include&lt;/code&gt; and when executing script files directly from the command line as the first argument.  &lt;/li&gt;
&lt;li&gt;Pure is very lightweight and will always be a thin layer on top of Roslyn/.NET runtime.  &lt;/li&gt;
&lt;li&gt;CLI REPL; GUI Notebook.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;basic-syntax&quot;&gt;Basic Syntax&lt;/h4&gt;
&lt;p&gt;Certain macros/syntax are provided to implement language-level scripting constructs:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=8123867204314969076&quot; alt=&quot;Table 2&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;summary-4&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Be aware that adopting a scripting system - any scripting system - comes with significant risks. It's wise to plan carefully and pick the best tools for the task at hand.&lt;/p&gt;
&lt;h3 id=&quot;references-4&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Download page for Pure 2 on &lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox.itch.io/pure-2&quot;&gt;Methodox Itch.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Official Page for Pure 2 on &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Utilities/CLI/Pure&quot;&gt;Methodox Wiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>C#</category>
<guid isPermaLink="true">https://coderlegion.com/4264/devlog-20250724-c-scripting-pure-2-updated-some-recap</guid>
<pubDate>Fri, 25 Jul 2025 01:39:40 +0000</pubDate>
</item>
<item>
<title>DevLog 20250711: Flappy Bird in Divooka! (Sneak Peak)</title>
<link>https://coderlegion.com/3910/devlog-20250711-flappy-bird-in-divooka-sneak-peak</link>
<description>&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o0azkmv7eu52o89j04yp.jpg&quot; alt=&quot;Flappy Bird Demo&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;sneak-peek&quot;&gt;Sneak Peek&lt;/h3&gt;
&lt;p&gt;I'm very excited to share some updates, along with this early demo video of making and playing Flappy Bird in Divooka! Please take a look at our &lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/KPrjm8zWBuQ&quot;&gt;YouTube&lt;/a&gt; for the HD video!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eg75q0dnksupzisf94kj.png&quot; alt=&quot;Graph Setup&quot;&gt;&lt;/p&gt;
&lt;p&gt;There's a lot of technology packed into and behind this little demo.&lt;br&gt;
Sure, there are hacks here and there - like all video games - and there's still tons to be done to make it production-ready and robust. But I think this is a good, solid proof-of-concept! We are at least at parity with what Unreal Engine Blueprint can achieve for interactive 2D games!&lt;/p&gt;
&lt;h3 id=&quot;overview-7&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;Divooka by Methodox is our flagship visual programming language and programming platform.&lt;/p&gt;
&lt;p&gt;In the YouTube video, I shared a quick demo of the program setup, graph layout, and runtime behavior of Flappy Bird made in Divooka.&lt;/p&gt;
&lt;p&gt;This setup is entirely done in the Divooka procedural context as a proof of concept for competitive runtime performance, suitability for practical program development, general applicability of the Divooka language, and the availability of Glaze! - our real-time graphical application API.&lt;/p&gt;
&lt;p&gt;A detailed tutorial, along with all resource files, will be shared under the &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Frameworks/Glaze/Tutorials/FlappyBird&quot;&gt;Methodox Wiki&lt;/a&gt; when the time comes.&lt;/p&gt;
&lt;h3 id=&quot;dev-process&quot;&gt;Dev Process&lt;/h3&gt;
&lt;p&gt;The latest update on the &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250708-procedural-context-visual-debugger-in-divooka-4fb7&quot;&gt;procedural context visual debugger&lt;/a&gt; has been really helpful!&lt;/p&gt;
&lt;p&gt;For the purpose of this particular demo, I started in Divooka, then realized lots of missing pieces. So I drafted the API for Glaze! first, then did a benchmark in C# - so actually, there is a pure C# implementation before the Divooka implementation.&lt;/p&gt;
&lt;p&gt;The urgent need for &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250710-generics-in-divooka-49e6&quot;&gt;generics&lt;/a&gt; becomes evident when we don't have structs and other OOP constructs in Divooka - we need a way to contain tuples of values in collections.&lt;/p&gt;
&lt;h3 id=&quot;commentary&quot;&gt;Commentary&lt;/h3&gt;
&lt;p&gt;Unreal Engine can already do it - and we're just catching up. On the other hand, Unreal Engine actually packs a similar size - but with way more features! Reducing distribution size is something that will require tremendous effort from us.&lt;/p&gt;
&lt;h3 id=&quot;references-5&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Learn more about procedural context: &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Divooka/Language/ProceduralContext&quot;&gt;Methodox Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;YouTube: &lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/KPrjm8zWBuQ&quot;&gt;Divooka Visual Programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Tutorial on making Flappy Bird in Divooka(WIP): &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Frameworks/Glaze/Tutorials/FlappyBird&quot;&gt;Methodox Wiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Divooka</category>
<guid isPermaLink="true">https://coderlegion.com/3910/devlog-20250711-flappy-bird-in-divooka-sneak-peak</guid>
<pubDate>Fri, 11 Jul 2025 19:38:55 +0000</pubDate>
</item>
<item>
<title>DevLog 20250711: Limitations of Scratch</title>
<link>https://coderlegion.com/3903/devlog-20250711-limitations-of-scratch</link>
<description>&lt;p&gt;Revision: 1&lt;br&gt;
Last update: 2025-07-11&lt;br&gt;
Tags: Reference&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Scratch is top 20 most used programming language in &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.tiobe.com/tiobe-index/scratch/&quot;&gt;TIOBE index&lt;/a&gt;. Understanding limitations of other programming systems is key to improving our own programming language (Divooka).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Scratch targets a different audience than we do, however, it's very well-designed, highly accessible and well adopted. Due to its visual paradigm similarity, we use it as a point of comparison for Divooka in terms of fidelity.&lt;/p&gt;
&lt;p&gt;Scratch is designed for learning and small‐scale animations or games. It does this job really well.&lt;/p&gt;
&lt;h3 id=&quot;advantages&quot;&gt;Advantages&lt;/h3&gt;
&lt;p&gt;Here are some of the major advantages of using Scratch as a first programming environment:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Intuitive, Low-Barrier Visual Interface&lt;/strong&gt;&lt;br&gt;
Scratch's drag-and-drop block system eliminates syntax errors and lets learners focus on concepts rather than typing rules. Its colorful, tactile blocks and immediate feedback make it fun and engaging from the very first project.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fosters Creativity and Engagement&lt;/strong&gt;&lt;br&gt;
With Scratch, users can build interactive stories, animations, games, music and art - mixing sprites, sound and backgrounds to realize their ideas. This &quot;Imagine-Program-Share&quot; cycle encourages open-ended exploration and artistic expression.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Promotes Computational Thinking &amp;amp; Problem-Solving&lt;/strong&gt;&lt;br&gt;
By snapping together blocks for loops, conditionals, variables and events, students naturally learn decomposition, algorithmic thinking and debugging strategies. Scratch even introduces parallelism - each sprite runs its own scripts - so beginners see concurrency concepts early on.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vibrant, Collaborative Online Community&lt;/strong&gt;&lt;br&gt;
The Scratch website hosts millions of &quot;Scratchers&quot; who share, remix and comment on projects. Community-run studios and design challenges foster collaboration, peer feedback and iterative learning - building social-emotional as well as coding skills.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Highly Accessible &amp;amp; Inclusive&lt;/strong&gt;&lt;br&gt;
Scratch is completely free and runs in any modern web browser (or as an offline desktop app). It's translated into over 50 languages and its block-based format lowers barriers for learners with dyslexia, limited typing skills or other challenges.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Educationally Aligned &amp;amp; Cross-Disciplinary&lt;/strong&gt;&lt;br&gt;
Used in thousands of schools worldwide (from elementary through college), Scratch supports math, science, history and language arts lessons through interactive simulations and tutorials. Educators leverage it to visualize abstract concepts and design project-based assessments.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Strong Foundation for Future Coding&lt;/strong&gt;&lt;br&gt;
Concepts mastered in Scratch - sequencing, events, data structures - translate directly to text-based languages like Python and Java. Many curricula use Scratch as a stepping-stone to more advanced computer science topics and career pathways.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Builds Confidence &amp;amp; Persistence&lt;/strong&gt;&lt;br&gt;
The instant, visual nature of Scratch's feedback loop - see your sprite move as soon as you snap a block - encourages learners to experiment, iterate and persist through challenges, laying the groundwork for resilient problem-solving habits.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;limitations-1&quot;&gt;Limitations&lt;/h3&gt;
&lt;p&gt;It runs into trouble once you're trying to build &quot;real&quot; software. Below we enumerate some major pain points, and some ideas for how to overcome each.&lt;/p&gt;
&lt;h4 id=&quot;1-monolithic-flat-project-structure&quot;&gt;1. Monolithic, Flat Project Structure&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What happens in Scratch:&lt;/strong&gt;&lt;br&gt;
All your code lives in sprites and the Stage, with no real namespace or folder hierarchies. It's like having every chapter of your novel printed on Post-its stuck to one wall.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why it's a problem at scale:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Hard to organize hundreds of scripts&lt;/li&gt;
&lt;li&gt;Difficult to see &quot;what calls what&quot;&lt;/li&gt;
&lt;li&gt;Prone to name collisions when you start copying-and-pasting blocks&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How to address it:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Custom block libraries&lt;/strong&gt;: Treat each custom block as a mini-module, and group them carefully by sprite (e.g. &quot;Physics,&quot; &quot;UI,&quot; &quot;Data&quot;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transpile to text code&lt;/strong&gt;: Export your project to JavaScript or Python (via tools like TurboWarp or external transpilers) so you can organize into real files and folders.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adopt a hybrid editor&lt;/strong&gt;: Use tools like MakeCode or Blockly that let you switch between blocks and text, and support hierarchical file structures.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;2-no-real-version-control&quot;&gt;2. No Real Version Control&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What happens in Scratch:&lt;/strong&gt;&lt;br&gt;
You &quot;Save&quot; or &quot;Remix,&quot; and history is opaque. There's no Git diff you can read or &quot;branch&quot; your experiments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why it's a problem at scale:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Team collaboration stalls&lt;/li&gt;
&lt;li&gt;Risk of overwriting each other's work&lt;/li&gt;
&lt;li&gt;Hard to roll back a specific change (e.g. &quot;Why did the scoring logic break on June 10?&quot;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How to address it:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Structured exports&lt;/strong&gt;: Regularly export your project as SB3 and check those binary files into a Git repo with clear commit messages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Text-based backups&lt;/strong&gt;: Leverage a transpiler to convert Scratch blocks into JavaScript or Python, then use Git on the resulting code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use a visual-code platform with Git support&lt;/strong&gt;: Divooka or alternatives (e.g. Node-RED, Blockly) that let you commit changes as text under the hood.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;3-limited-language-features&quot;&gt;3. Limited Language Features&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What happens in Scratch:&lt;/strong&gt;&lt;br&gt;
You get basic loops, conditionals and variables, but no classes, no modules, no first-class functions, no generics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why it's a problem at scale:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;You can't encapsulate complex behavior neatly&lt;/li&gt;
&lt;li&gt;Difficult to share sophisticated libraries (e.g. a math library or HTTP client)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How to address it:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Extension mechanism&lt;/strong&gt;: Build or use native extensions (ScratchX or TurboWarp extensions) to expose advanced APIs (file I/O, networking, data structures).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hybrid blocks + text&lt;/strong&gt;: Offer a &quot;power user&quot; mode where blocks desugar into real functions and classes in a host language.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Migrate critical components&lt;/strong&gt;: For performance- or feature-critical parts (e.g. physics engine), write them in C# or Python and call them via an FFI-style bridge.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;4-performance-amp-resource-constraints&quot;&gt;4. Performance &amp;amp; Resource Constraints&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What happens in Scratch:&lt;/strong&gt;&lt;br&gt;
All scripts run in a single-threaded VM with modest optimization; large loops or many clones can slow everything to a crawl.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why it's a problem at scale:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Complex simulations or data processing become unbearably slow&lt;/li&gt;
&lt;li&gt;No easy way to leverage multiple CPU cores or GPU&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How to address it:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Chunk and yield&lt;/strong&gt;: Break big tasks into small chunks and let the scheduler breathe (e.g. use &lt;code&gt;wait 0 secs&lt;/code&gt; strategically).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Native compute modules&lt;/strong&gt;: Offload heavy work into native extensions written in C/C++/Rust.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parallelizable architecture&lt;/strong&gt;: Move to a platform (like Divooka) that supports true multithreading or asynchronous tasks.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;5-debugging-amp-testing&quot;&gt;5. Debugging &amp;amp; Testing&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What happens in Scratch:&lt;/strong&gt;&lt;br&gt;
You're limited to watching sprites move, sprinkling in &lt;code&gt;say&lt;/code&gt; blocks or using the &quot;block watcher.&quot; There's no unit-test framework or breakpoint debugger.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why it's a problem at scale:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Bugs hide in tangled event chains&lt;/li&gt;
&lt;li&gt;Hard to write repeatable regression tests&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How to address it:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Logging extension&lt;/strong&gt;: Create blocks that log to an external console or file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test harness in code&lt;/strong&gt;: Transpile critical logic into plain code and write unit tests (e.g. using pytest or NUnit).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visual breakpoint system&lt;/strong&gt;: Enhance your tool (or use TurboWarp) to allow pausing and inspecting variables at runtime.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;6-collaboration-amp-team-workflow&quot;&gt;6. Collaboration &amp;amp; Team Workflow&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What happens in Scratch:&lt;/strong&gt;&lt;br&gt;
Mostly a single-user hobby environment with &quot;Remix&quot; as the only share option.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why it's a problem at scale:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;No real task assignment or issue tracking&lt;/li&gt;
&lt;li&gt;Unstructured remixing leads to forks with no merge path&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How to address it:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Integrated project management&lt;/strong&gt;: Embed issue-tracking metadata in your SBC file or alongside exports.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Merge tooling&lt;/strong&gt;: Use text-based diffs (after transpilation) and Git-based PR workflows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Role-based access&lt;/strong&gt;: Adopt an environment where you can assign &quot;developer,&quot; &quot;reviewer,&quot; &quot;tester&quot; roles, with locking on code units.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;bottom-line&quot;&gt;Bottom Line&lt;/h4&gt;
&lt;p&gt;Scratch is an incredible gateway for learning programming fundamentals and building quick prototypes. But when you're talking hundreds of sprites, gigabytes of assets, dozens of collaborators, or millions of lines of logic… you need:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Modularity &amp;amp; namespaces&lt;/strong&gt; (think packages)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Text-based exports for version control&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advanced language features &amp;amp; extensions&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance optimizations via native code&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Real debugging &amp;amp; testing frameworks&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Team collaboration workflows&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Many of these gaps can be addressed by extended platforms like &lt;strong&gt;Snap!&lt;/strong&gt;, &lt;strong&gt;Blockly/MakeCode&lt;/strong&gt; and &lt;strong&gt;TurboWarp&lt;/strong&gt;.&lt;/p&gt;
</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/3903/devlog-20250711-limitations-of-scratch</guid>
<pubDate>Fri, 11 Jul 2025 12:55:09 +0000</pubDate>
</item>
<item>
<title>DevLog 20250710: ComfyUI API</title>
<link>https://coderlegion.com/3886/devlog-20250710-comfyui-api</link>
<description>&lt;p&gt;ComfyUI's architecture can seem opaque at first glance, particularly for those approaching it from another platform or without a deep dive into its internals. This has led to some overly complex integration attempts. In reality, however, ComfyUI offers a clean, extensible interface. Every running instance acts as a full-featured API server  -  in fact, the ComfyUI frontend is essentially just a visual client layered on top of this API.&lt;/p&gt;
&lt;h3 id=&quot;overview-on-architecture&quot;&gt;Overview On Architecture&lt;/h3&gt;
&lt;p&gt;ComfyUI is fundamentally a node-based interface and inference engine for generative AI, designed with a clear separation between frontend and backend. Under the hood, every ComfyUI process spins up a Python HTTP server that exposes both REST and WebSocket endpoints, meaning the familiar graphical canvas you interact with is simply one of many clients talking to this API-first backend.&lt;/p&gt;
&lt;p&gt;Because the UI itself is just a &quot;front end&quot; for these same endpoints, you can swap it out entirely or layer additional tooling on top without touching the core. For example, the backend defines routes like &lt;code&gt;/ws&lt;/code&gt; (a WebSocket endpoint for real-time status and currently executing-node updates) and &lt;code&gt;/prompt&lt;/code&gt; (a REST endpoint to enqueue a workflow), which are the exact primitives any programmatic client or custom UI uses to interact with ComfyUI.&lt;/p&gt;
&lt;p&gt;Beyond the core node types, ComfyUI's architecture is highly extensible via &quot;API Nodes,&quot; special nodes that connect to closed-source or third-party AI services through external HTTP APIs. These API Nodes behave just like any other node in a workflow - once installed, they’re automatically discoverable through the same server endpoints, underscoring the system’s API-first design and seamless plugin support.&lt;/p&gt;
&lt;h3 id=&quot;the-api&quot;&gt;The API&lt;/h3&gt;
&lt;p&gt;There are two primary usage patterns for interacting with ComfyUI at the API level:&lt;/p&gt;
&lt;h4 id=&quot;editor-like-interface&quot;&gt;Editor-like Interface&lt;/h4&gt;
&lt;p&gt;In this mode, you explicitly build and manipulate workflow graphs before executing them. ComfyUI’s GitHub repository includes a &lt;code&gt;script_examples&lt;/code&gt; folder with Python scripts that demonstrate how to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Discover&lt;/strong&gt; all available node types and their parameters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assemble&lt;/strong&gt; a workflow graph as a JSON object (mapping node IDs to &lt;code&gt;class_type&lt;/code&gt;, &lt;code&gt;inputs&lt;/code&gt;, and optional metadata).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Save&lt;/strong&gt; or &lt;strong&gt;load&lt;/strong&gt; these JSON graphs to and from disk.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Submit&lt;/strong&gt; the graph to the server via HTTP (e.g. to &lt;code&gt;/prompt&lt;/code&gt;) for execution.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because everything - node definitions, graph structures, and execution commands - is pure JSON over HTTP, you can implement a full-featured visual editor or headless pipeline in any language or framework that supports HTTP and JSON ([GitHub][5]).&lt;/p&gt;
&lt;h4 id=&quot;runtime-like-interface&quot;&gt;Runtime-like Interface&lt;/h4&gt;
&lt;p&gt;This simpler mode treats ComfyUI as a pure inference engine. Typically, you maintain a library of pre-built workflow JSON templates, and at runtime:&lt;/p&gt;
&lt;p&gt;Step 1. &lt;strong&gt;POST&lt;/strong&gt; your chosen graph JSON to &lt;code&gt;http://&amp;lt;host&amp;gt;:&amp;lt;port&amp;gt;/prompt&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /prompt
Content-Type: application/json

{
 &quot;prompt&quot;: { …complete workflow JSON… },
 &quot;client_id&quot;: &quot;optional-client-id&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On success, you receive a &lt;code&gt;prompt_id&lt;/code&gt; to track the job.&lt;/p&gt;
&lt;p&gt;Step 2. &lt;strong&gt;Track progress&lt;/strong&gt; by opening a WebSocket connection to &lt;code&gt;ws://&amp;lt;host&amp;gt;:&amp;lt;port&amp;gt;/ws&lt;/code&gt;. This provides messages about which node is currently executing, along with overall status updates - ideal for real-time visibility into long-running workflows.&lt;/p&gt;
&lt;p&gt;Step 3. &lt;strong&gt;Retrieve results&lt;/strong&gt; (e.g. output image filenames and metadata) via:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /history/&amp;lt;prompt_id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This returns a JSON payload listing saved images, their directories, and any workflow metadata needed to fetch or display the outputs.&lt;/p&gt;
&lt;p&gt;This runtime-style pattern is ideal for integrating ComfyUI into production applications, batch-processing pipelines, or serverless functions, as it requires only minimal client logic.&lt;/p&gt;
&lt;p&gt;Primary endpoints in use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://127.0.0.1:8188/prompt&lt;/code&gt; - send prompt into execution queue&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://127.0.0.1:8188/history&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://127.0.0.1:8188/history/{prompt_id}&lt;/code&gt; – returns results when complete&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://127.0.0.1:8188/view&lt;/code&gt; - retrieve images&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://127.0.0.1:8188/queue&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For deeper insight, studying the server source code is highly instructive:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/comfyanonymous/ComfyUI/blob/master/server.py&quot;&gt;https://github.com/comfyanonymous/ComfyUI/blob/master/server.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/comfyanonymous/ComfyUI/blob/master/main.py#L221&quot;&gt;https://github.com/comfyanonymous/ComfyUI/blob/master/main.py#L221&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;summary-5&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;To summarize, working with ComfyUI programmatically is straightforward - simply use the &lt;code&gt;/prompt&lt;/code&gt; endpoint to submit workflows, as illustrated below.&lt;/p&gt;
&lt;p&gt;Step 1. &lt;strong&gt;Get the node catalogue&lt;/strong&gt; (optional but useful): &lt;code&gt;GET /object_info&lt;/code&gt; returns the entire node class library, including inputs, outputs, default values, and documentation - perfect for editor autocompletion or scripting.&lt;/p&gt;
&lt;p&gt;Step 2. &lt;strong&gt;Build or load a workflow JSON&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create it using the GUI &lt;strong&gt;or&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Assemble it programmatically (see &lt;code&gt;script_examples/build_json.py&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Step 3. &lt;strong&gt;Submit the workflow&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -X POST http://127.0.0.1:8188/prompt \
-H 'Content-Type: application/json' \
-d '{&quot;prompt&quot;: YOUR_WORKFLOW_JSON,
 &quot;client_id&quot;: &quot;my-tool&quot;}'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On success, the server returns: &lt;code&gt;{&quot;prompt_id&quot;: &quot;&amp;lt;uuid&amp;gt;&quot;}&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Step 4. &lt;strong&gt;Track execution progress (real-time)&lt;/strong&gt;:&lt;br&gt;
Open a WebSocket to &lt;code&gt;ws://127.0.0.1:8188/ws?clientId=my-tool&lt;/code&gt; and listen for messages:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{ &quot;type&quot;: &quot;executing&quot;, &quot;data&quot;: {&quot;node&quot;: 42} }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Useful for logging, progress bars, or live UIs.&lt;/p&gt;
&lt;p&gt;Step 5. &lt;strong&gt;Fetch results&lt;/strong&gt;:&lt;br&gt;
Once execution is complete, request &lt;code&gt;GET /history/&amp;lt;prompt_id&amp;gt;&lt;/code&gt; to retrieve a list of output files and metadata. To download or stream an image, use &lt;code&gt;/view?filename=...&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This flow - &lt;em&gt;build JSON → POST → (optional WebSocket) → GET history&lt;/em&gt; - is versatile enough to power both editor-based prototyping and production-scale batch jobs.&lt;/p&gt;
&lt;h4 id=&quot;quick-reference&quot;&gt;Quick Reference&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Verb   &lt;/th&gt;&lt;th&gt;Endpoint        &lt;/th&gt;&lt;th&gt;Purpose&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;POST&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;/prompt&lt;/code&gt;       &lt;/td&gt;&lt;td&gt;Queue a workflow&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;  &lt;/td&gt;&lt;td&gt;&lt;code&gt;/history/&amp;lt;id&amp;gt;&lt;/code&gt; &lt;/td&gt;&lt;td&gt;Fetch results and metadata&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;  &lt;/td&gt;&lt;td&gt;&lt;code&gt;/object_info&lt;/code&gt;  &lt;/td&gt;&lt;td&gt;Explore available nodes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;  &lt;/td&gt;&lt;td&gt;&lt;code&gt;/queue&lt;/code&gt;        &lt;/td&gt;&lt;td&gt;View queued or running jobs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;WS&lt;/code&gt;   &lt;/td&gt;&lt;td&gt;&lt;code&gt;/ws&lt;/code&gt;           &lt;/td&gt;&lt;td&gt;Live status updates&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;references-6&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://9elements.com/blog/hosting-a-comfyui-workflow-via-api/&quot;&gt;Hosting a ComfyUI Workflow via API&lt;/a&gt; by 9elements&lt;/li&gt;
&lt;li&gt;Official &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/comfyanonymous/ComfyUI/tree/master/script_examples&quot;&gt;ComfyUI API Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Discussion on ComfyUI Prompt API Endpoints: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/comfyanonymous/ComfyUI/issues/2110&quot;&gt;Github Issue 2110&lt;/a&gt;&lt;br&gt;
and &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/comfyanonymous/ComfyUI/issues/6607&quot;&gt;Github Issue 6607&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A video walkthrough on ComfyUI API by Methodox: &lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/V9HC-RErzkY&quot;&gt;Divooka Visual Programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Use ComfyUI in Divooka: &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Divooka/Topics/ComfyUI&quot;&gt;Methodox Wiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;appendix---requests&quot;&gt;Appendix - Requests&lt;/h3&gt;
&lt;p&gt;Because it's very useful and rarely available in such shocking detail, here I am providing full requests for quick reference.&lt;/p&gt;
&lt;p&gt;Request and responses:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# POST http://146.190.245.196:8188/prompt
{
  &quot;prompt&quot;: {
    &quot;3&quot;: {
      &quot;inputs&quot;: {
        &quot;seed&quot;: 1044669037100678,
        &quot;steps&quot;: 20,
        &quot;cfg&quot;: 8,
        &quot;sampler_name&quot;: &quot;euler&quot;,
        &quot;scheduler&quot;: &quot;normal&quot;,
        &quot;denoise&quot;: 1,
        &quot;model&quot;: [&quot;4&quot;, 0],
        &quot;positive&quot;: [&quot;6&quot;, 0],
        &quot;negative&quot;: [&quot;7&quot;, 0],
        &quot;latent_image&quot;: [&quot;5&quot;, 0]
      },
      &quot;class_type&quot;: &quot;KSampler&quot;,
      &quot;_meta&quot;: {
        &quot;title&quot;: &quot;KSampler&quot;
      }
    },
    &quot;4&quot;: {
      &quot;inputs&quot;: {
        &quot;ckpt_name&quot;: &quot;v1-5-pruned-emaonly-fp16.safetensors&quot;
      },
      &quot;class_type&quot;: &quot;CheckpointLoaderSimple&quot;,
      &quot;_meta&quot;: {
        &quot;title&quot;: &quot;Load Checkpoint&quot;
      }
    },
    &quot;5&quot;: {
      &quot;inputs&quot;: {
        &quot;width&quot;: 512,
        &quot;height&quot;: 512,
        &quot;batch_size&quot;: 1
      },
      &quot;class_type&quot;: &quot;EmptyLatentImage&quot;,
      &quot;_meta&quot;: {
        &quot;title&quot;: &quot;Empty Latent Image&quot;
      }
    },
    &quot;6&quot;: {
      &quot;inputs&quot;: {
        &quot;text&quot;: &quot;beautiful scenery nature glass bottle landscape, , purple galaxy bottle,&quot;,
        &quot;clip&quot;: [&quot;4&quot;, 1]
      },
      &quot;class_type&quot;: &quot;CLIPTextEncode&quot;,
      &quot;_meta&quot;: {
        &quot;title&quot;: &quot;CLIP Text Encode (Prompt)&quot;
      }
    },
    &quot;7&quot;: {
      &quot;inputs&quot;: {
        &quot;text&quot;: &quot;text, watermark&quot;,
        &quot;clip&quot;: [&quot;4&quot;, 1]
      },
      &quot;class_type&quot;: &quot;CLIPTextEncode&quot;,
      &quot;_meta&quot;: {
        &quot;title&quot;: &quot;CLIP Text Encode (Prompt)&quot;
      }
    },
    &quot;8&quot;: {
      &quot;inputs&quot;: {
        &quot;samples&quot;: [&quot;3&quot;, 0],
        &quot;vae&quot;: [&quot;4&quot;, 2]
      },
      &quot;class_type&quot;: &quot;VAEDecode&quot;,
      &quot;_meta&quot;: {
        &quot;title&quot;: &quot;VAE Decode&quot;
      }
    },
    &quot;9&quot;: {
      &quot;inputs&quot;: {
        &quot;filename_prefix&quot;: &quot;ComfyUI&quot;,
        &quot;images&quot;: [&quot;8&quot;, 0]
      },
      &quot;class_type&quot;: &quot;SaveImage&quot;,
      &quot;_meta&quot;: {
        &quot;title&quot;: &quot;Save Image&quot;
      }
    }
  }
}
# Response
{
  &quot;prompt_id&quot;: &quot;26bc628d-7ab2-43e6-8a67-d6a793e7fbcc&quot;,
  &quot;number&quot;: 1,
  &quot;node_errors&quot;: {}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# GET http://146.190.245.196:8188/history/26bc628d-7ab2-43e6-8a67-d6a793e7fbcc
{
  &quot;26bc628d-7ab2-43e6-8a67-d6a793e7fbcc&quot;: {
    &quot;prompt&quot;: [
      1,
      &quot;26bc628d-7ab2-43e6-8a67-d6a793e7fbcc&quot;,
      {
        &quot;3&quot;: {
          &quot;inputs&quot;: {
            &quot;seed&quot;: 1044669037100678,
            &quot;steps&quot;: 20,
            &quot;cfg&quot;: 8.0,
            &quot;sampler_name&quot;: &quot;euler&quot;,
            &quot;scheduler&quot;: &quot;normal&quot;,
            &quot;denoise&quot;: 1.0,
            &quot;model&quot;: [
              &quot;4&quot;,
              0
            ],
            &quot;positive&quot;: [
              &quot;6&quot;,
              0
            ],
            &quot;negative&quot;: [
              &quot;7&quot;,
              0
            ],
            &quot;latent_image&quot;: [
              &quot;5&quot;,
              0
            ]
          },
          &quot;class_type&quot;: &quot;KSampler&quot;,
          &quot;_meta&quot;: {
            &quot;title&quot;: &quot;KSampler&quot;
          }
        },
        &quot;4&quot;: {
          &quot;inputs&quot;: {
            &quot;ckpt_name&quot;: &quot;v1-5-pruned-emaonly-fp16.safetensors&quot;
          },
          &quot;class_type&quot;: &quot;CheckpointLoaderSimple&quot;,
          &quot;_meta&quot;: {
            &quot;title&quot;: &quot;Load Checkpoint&quot;
          }
        },
        &quot;5&quot;: {
          &quot;inputs&quot;: {
            &quot;width&quot;: 512,
            &quot;height&quot;: 512,
            &quot;batch_size&quot;: 1
          },
          &quot;class_type&quot;: &quot;EmptyLatentImage&quot;,
          &quot;_meta&quot;: {
            &quot;title&quot;: &quot;Empty Latent Image&quot;
          }
        },
        &quot;6&quot;: {
          &quot;inputs&quot;: {
            &quot;text&quot;: &quot;beautiful scenery nature glass bottle landscape, , purple galaxy bottle,&quot;,
            &quot;clip&quot;: [
              &quot;4&quot;,
              1
            ]
          },
          &quot;class_type&quot;: &quot;CLIPTextEncode&quot;,
          &quot;_meta&quot;: {
            &quot;title&quot;: &quot;CLIP Text Encode (Prompt)&quot;
          }
        },
        &quot;7&quot;: {
          &quot;inputs&quot;: {
            &quot;text&quot;: &quot;text, watermark&quot;,
            &quot;clip&quot;: [
              &quot;4&quot;,
              1
            ]
          },
          &quot;class_type&quot;: &quot;CLIPTextEncode&quot;,
          &quot;_meta&quot;: {
            &quot;title&quot;: &quot;CLIP Text Encode (Prompt)&quot;
          }
        },
        &quot;8&quot;: {
          &quot;inputs&quot;: {
            &quot;samples&quot;: [
              &quot;3&quot;,
              0
            ],
            &quot;vae&quot;: [
              &quot;4&quot;,
              2
            ]
          },
          &quot;class_type&quot;: &quot;VAEDecode&quot;,
          &quot;_meta&quot;: {
            &quot;title&quot;: &quot;VAE Decode&quot;
          }
        },
        &quot;9&quot;: {
          &quot;inputs&quot;: {
            &quot;filename_prefix&quot;: &quot;ComfyUI&quot;,
            &quot;images&quot;: [
              &quot;8&quot;,
              0
            ]
          },
          &quot;class_type&quot;: &quot;SaveImage&quot;,
          &quot;_meta&quot;: {
            &quot;title&quot;: &quot;Save Image&quot;
          }
        }
      },
      {},
      [
        &quot;9&quot;
      ]
    ],
    &quot;outputs&quot;: {
      &quot;9&quot;: {
        &quot;images&quot;: [
          {
            &quot;filename&quot;: &quot;ComfyUI_00002_.png&quot;,
            &quot;subfolder&quot;: &quot;&quot;,
            &quot;type&quot;: &quot;output&quot;
          }
        ]
      }
    },
    &quot;status&quot;: {
      &quot;status_str&quot;: &quot;success&quot;,
      &quot;completed&quot;: true,
      &quot;messages&quot;: [
        [
          &quot;execution_start&quot;,
          {
            &quot;prompt_id&quot;: &quot;26bc628d-7ab2-43e6-8a67-d6a793e7fbcc&quot;,
            &quot;timestamp&quot;: 1745105352150
          }
        ],
        [
          &quot;execution_cached&quot;,
          {
            &quot;nodes&quot;: [
              &quot;4&quot;,
              &quot;5&quot;,
              &quot;6&quot;,
              &quot;7&quot;
            ],
            &quot;prompt_id&quot;: &quot;26bc628d-7ab2-43e6-8a67-d6a793e7fbcc&quot;,
            &quot;timestamp&quot;: 1745105352151
          }
        ],
        [
          &quot;execution_success&quot;,
          {
            &quot;prompt_id&quot;: &quot;26bc628d-7ab2-43e6-8a67-d6a793e7fbcc&quot;,
            &quot;timestamp&quot;: 1745105352778
          }
        ]
      ]
    },
    &quot;meta&quot;: {
      &quot;9&quot;: {
        &quot;node_id&quot;: &quot;9&quot;,
        &quot;display_node&quot;: &quot;9&quot;,
        &quot;parent_node&quot;: null,
        &quot;real_node_id&quot;: &quot;9&quot;
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# GET http://146.190.245.196:8188/view?filename=ComfyUI_00002_.png&amp;amp;subfolder=&amp;amp;type=output
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# POST http://127.0.0.1:8188/upload/image
Multipart with `image` payload.
{
&quot;name&quot;: &quot;Test.jpg&quot;,
&quot;subfolder&quot;: &quot;&quot;,
&quot;type&quot;: &quot;input&quot;
}
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Generative AI</category>
<guid isPermaLink="true">https://coderlegion.com/3886/devlog-20250710-comfyui-api</guid>
<pubDate>Thu, 10 Jul 2025 15:05:51 +0000</pubDate>
</item>
<item>
<title>The Divooka Way - Part 1: Philosophically, How Exactly is Divooka Different and Useful</title>
<link>https://coderlegion.com/3862/the-divooka-way-part-1-philosophically-how-exactly-is-divooka-different-and-useful</link>
<description>&lt;ul&gt;
&lt;li&gt;Full title: The Divooka Way — Part 1: Philosophically, How Exactly is Divooka Different and Useful Compared to Plain Good Code API&lt;/li&gt;
&lt;li&gt;Tags: Visual Programming, Developer Tools, API Design, Software Architecture, Programming Paradigms, NVI, No-Code / Low-Code, Programming Philosophy, Tool Economy  &lt;/li&gt;
&lt;li&gt;Target Audience: Software Architects, Engine and Tool Developers, Programming Educators and Curriculum Designers, Low-Code/No-Code Platform Researchers, Senior Developers interested in alternative programming models, Developers interested in visual alternatives to traditional code*&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;abstract&quot;&gt;Abstract&lt;/h3&gt;
&lt;p&gt;Traditional programming uses text to represent program logic. Existing visual design platforms offer varying levels of programmability but generally focus on building specific kinds of applications. From a production-use perspective, Divooka represents a significant step forward in how users build and interact with software - by combining tool-building, data handling, and program logic under a single, coherent interface. This unified approach aims to deliver a substantial productivity boost.&lt;/p&gt;
&lt;h3 id=&quot;overview-8&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;If we already have a really good library - just as we have high-quality commercial software - does it still matter what language or environment we use?&lt;/p&gt;
&lt;p&gt;In theory, it shouldn't. In practice, it absolutely does.&lt;/p&gt;
&lt;p&gt;Pretty much everything imaginable already exists for C++, often under GNU licenses. But that doesn't mean a Python, C#, or Java developer can easily access or use those resources. Even when libraries are available for a target language, usage may not be straightforward. Licensing, API design, and documentation all come into play.&lt;/p&gt;
&lt;p&gt;Still, let's imagine we have a well-written, freely accessible, multi-language-bound, well-documented, and easy-to-use library. Does it then matter &lt;em&gt;how&lt;/em&gt; we use it?&lt;/p&gt;
&lt;p&gt;That leads us to the core question:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If we already have a really good API, why not just use it in C#, Python, C++, Lua, or Pure?&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-proof-of-concept&quot;&gt;The Proof of Concept&lt;/h3&gt;
&lt;p&gt;To explore this, we can approach the question from three distinct perspectives:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The end user&lt;/li&gt;
&lt;li&gt;The program designer&lt;/li&gt;
&lt;li&gt;Everyday tool development and sharing&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;From the end user's perspective&lt;/strong&gt;, assuming the final deliverable is a polished CLI or GUI tool, it typically doesn't matter what language was used - as long as the interface is well-designed. Case closed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;From the program designer's perspective&lt;/strong&gt;, building anything sophisticated involves significant &quot;dry&quot; work - debugging, iteration, architectural decisions. Productivity depends largely on the quality of the debugger and IDE, and again, it's not strictly tied to the programming language.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But from the perspective of everyday tool use&lt;/strong&gt;, the question becomes more subtle. What do we mean by &quot;everyday tools&quot;? Broadly speaking, we mean:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tools built quickly to solve practical problems within days&lt;/li&gt;
&lt;li&gt;Tools that are easy to share and use by others&lt;/li&gt;
&lt;li&gt;Tools that are easy to iterate, improve, refactor, and eventually package as full software&lt;/li&gt;
&lt;li&gt;Tools that are maintainable - so that months later, we can return and still understand what we were doing, without extensive documentation effort&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To solve (1), you need extensive libraries.&lt;br&gt;
To solve (2), you need solid dependency and packaging mechanisms.&lt;br&gt;
To solve (3), you need simple syntax and easy refactoring.&lt;br&gt;
To solve (4), you need expressive, self-documenting code - or better yet, self-explanatory program design.&lt;/p&gt;
&lt;p&gt;It's in this third category - everyday tools - that &lt;strong&gt;Divooka stands out&lt;/strong&gt;. The features baked into Divooka's graph editor enable rapid development without sacrificing performance or scalability. The editor itself proves how smoothly things can run with minimal setup: just open a graph document, and it works.&lt;/p&gt;
&lt;h3 id=&quot;the-nvi&quot;&gt;The NVI&lt;/h3&gt;
&lt;p&gt;In Divooka, the primary mode of interaction is the &lt;strong&gt;Node Visual Interface (NVI)&lt;/strong&gt; - distinct from both CLI and GUI paradigms.&lt;/p&gt;
&lt;p&gt;Each node represents a functional unit, and the connections represent program or data flow. Unlike CLI, NVI offers &quot;autocomplete&quot; visually - everything is made explicit through connection and layout. Unlike a GUI, an NVI is composed entirely of nodes and avoids complex syntax structures.&lt;/p&gt;
&lt;p&gt;At the base is a &lt;strong&gt;node canvas&lt;/strong&gt;, and programs are built using what we call &lt;em&gt;node-driven design&lt;/em&gt; - a pattern that breaks software into node blocks, each representing a compositional or procedural component.&lt;/p&gt;
&lt;p&gt;The main &lt;strong&gt;disadvantage&lt;/strong&gt; of NVI compared to text-based programming is space inefficiency: nodes occupy screen real estate, reducing information density. But this is offset by improved &lt;strong&gt;readability&lt;/strong&gt;: the visual layout shows the exact dependencies between functional units - something much harder to grasp in linear text code.&lt;/p&gt;
&lt;p&gt;NVI becomes more powerful when it supports:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Subgraphs&lt;/strong&gt; – Logical groups of nodes encapsulated into single blocks. This is more compact than plain functions or classes and more intuitive than managing multiple files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extensible node visuals&lt;/strong&gt; – Nodes can be customized for specific data. For example, a &lt;em&gt;Table&lt;/em&gt; node can offer compact entry for 2D data, reducing friction.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;the-scripting-interface&quot;&gt;The Scripting Interface&lt;/h3&gt;
&lt;p&gt;At its core, the NVI exposes functionality in two ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Nodes represent standalone functional units in a Divooka document.&lt;/li&gt;
&lt;li&gt;A framework parses the interconnected nodes and derives behavior from the graph structure.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The key is &lt;strong&gt;interface availability&lt;/strong&gt; - file operations, media I/O, math routines, etc.&lt;/p&gt;
&lt;p&gt;The first use case is covered well by scripting languages like Python, Lua, or Jupyter.&lt;br&gt;
The second - interpreting a structured node graph into dynamic program behavior - is where traditional languages fall short, often requiring large, specialized frameworks (e.g., Streamlit for Python).&lt;/p&gt;
&lt;p&gt;With Divooka, the same graphical program often needs &lt;strong&gt;no changes at all&lt;/strong&gt;. A simple toggle in the host environment can completely redefine how the program behaves.&lt;/p&gt;
&lt;p&gt;Frameworks like &lt;strong&gt;Glaze&lt;/strong&gt;, &lt;strong&gt;Novella&lt;/strong&gt;, &lt;strong&gt;Ol'ista&lt;/strong&gt;, &lt;strong&gt;Slide Present&lt;/strong&gt;, and &lt;strong&gt;App Builder&lt;/strong&gt; (all part of Divooka Explore except &lt;strong&gt;App Builder&lt;/strong&gt;) rely heavily on metadata - information &lt;em&gt;not&lt;/em&gt; defined on the graph, but embedded in the document and interpreted by the host system.&lt;/p&gt;
&lt;p&gt;This separation - code in the graph, behavior defined by metadata - creates a powerful, &lt;strong&gt;data-driven&lt;/strong&gt; model that enables reuse, variation, and flexibility.&lt;/p&gt;
&lt;h3 id=&quot;on-the-matter-of-libraries&quot;&gt;On the Matter of Libraries&lt;/h3&gt;
&lt;p&gt;Not all useful features are readily available via libraries. And even when they are, compatibility issues, licenses, platform differences, and interoperability challenges often make reuse hard or impossible.&lt;/p&gt;
&lt;p&gt;At Methodox, we actively author and maintain a curated library set - &lt;strong&gt;toolboxes optimized for Divooka&lt;/strong&gt;. This represents a major investment, ensuring that as Divooka grows, users have an expanding, well-integrated set of native components tailored for node-driven environments.&lt;/p&gt;
&lt;h3 id=&quot;summary-6&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;From a scripting standpoint, Divooka may appear unremarkable: libraries are still authored in native code, and Divooka simply provides an interface layer.&lt;/p&gt;
&lt;p&gt;But &lt;strong&gt;methodologically&lt;/strong&gt;, Divooka offers a profound shift in how we &lt;em&gt;build&lt;/em&gt; and &lt;em&gt;interact&lt;/em&gt; with programs. It's as different as using a natural-language model to co-write your software.&lt;/p&gt;
&lt;p&gt;Divooka is a high-level, GUI-native, NVI-first programming system. Our belief is that this new format can significantly enhance &lt;strong&gt;productivity&lt;/strong&gt;, &lt;strong&gt;readability&lt;/strong&gt;, and &lt;strong&gt;maintainability&lt;/strong&gt; by making programs &lt;strong&gt;smaller&lt;/strong&gt;, &lt;strong&gt;clearer&lt;/strong&gt;, &lt;strong&gt;less error-prone&lt;/strong&gt;, and &lt;strong&gt;more intuitive&lt;/strong&gt;.&lt;/p&gt;
</description>
<category>Divooka</category>
<guid isPermaLink="true">https://coderlegion.com/3862/the-divooka-way-part-1-philosophically-how-exactly-is-divooka-different-and-useful</guid>
<pubDate>Wed, 09 Jul 2025 20:28:58 +0000</pubDate>
</item>
<item>
<title>One-Year Anniversary Reflection on The Development of Divooka</title>
<link>https://coderlegion.com/3838/one-year-anniversary-reflection-on-the-development-of-divooka</link>
<description>&lt;h3 id=&quot;overview-9&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Methodox Technologies, Inc.&lt;/strong&gt; - officially registered in Ontario at the end of July last year - has now been around for nearly a full year. Time has passed neither too quickly nor too slowly, moving along steadily as it always has. The biggest difference between running a company and doing personal side projects, in my view, can be captured with a comparison between &lt;em&gt;passion&lt;/em&gt; and &lt;em&gt;professionalism&lt;/em&gt;: when you do something out of passion, you choose based on interest; when you do something professionally, you commit regardless of mood or preference, working methodically toward clear goals and standards - day or night, rain or shine.&lt;/p&gt;
&lt;p&gt;To put it more concretely, the difference lies in &lt;em&gt;quality control&lt;/em&gt;. As someone with an engineering background, I'm not particularly gifted at self-promotion. What I can do - and do most naturally - is focusing on making a good product. Some friends have asked where the motivation comes from. Truthfully, I'm not entirely sure either. But like clockwork, I still wake up at 6:30 a.m. every day, ready to work.&lt;/p&gt;
&lt;h3 id=&quot;the-story&quot;&gt;The Story&lt;/h3&gt;
&lt;p&gt;When I first started working on &lt;strong&gt;Divooka&lt;/strong&gt;, I didn't envision it as a fully-fledged programming language. I wanted it to be general-purpose, yes - but not necessarily from the standpoint of a programming language. The goal was more about &lt;em&gt;decoupling&lt;/em&gt; the functionality from its environment, enabling it to run directly from the command line, and making the library design flexible enough to work beyond our immediate domain.&lt;/p&gt;
&lt;p&gt;Initially, the biggest problem I wanted to tackle was how to replace Excel. There are many ways to go about that - because Excel's greatest strength is also its weakness: it's &lt;em&gt;too&lt;/em&gt; powerful and flexible, which means it often lacks structure. And that chaos is what we wanted to bring order to. The first step was to build something on top of the spreadsheet concept - adding structure and rules, using something like object-oriented thinking to enable users to visually edit and link spreadsheet data.&lt;/p&gt;
&lt;p&gt;Had we stayed on that path, the Divooka interface today would probably look more like floating spreadsheet windows - similar to Apple's &lt;em&gt;Numbers&lt;/em&gt; - instead of the visual node-based editor you see now.&lt;/p&gt;
&lt;h3 id=&quot;the-divooka-way-1&quot;&gt;The Divooka Way&lt;/h3&gt;
&lt;p&gt;Back in 2019, I had already started exploring the idea of using general-purpose programming for GUI building. My approach then was more framework-oriented: tools that could generate UI wrappers based on flowcharts. These weren't new languages, but auxiliary features to make diagrams more reusable.&lt;/p&gt;
&lt;p&gt;In early 2024, we also explored two major directions: graphic annotation and online collaboration, inspired by modern design tools like &lt;em&gt;Figma&lt;/em&gt; and Google's cloud-based productivity suite.&lt;/p&gt;
&lt;p&gt;The ability to build complete applications wasn't something I initially envisioned - it seemed too ambitious for what started as a personal side project. But going full-time removed that ceiling. It gave us room to think bigger.&lt;/p&gt;
&lt;p&gt;One of the most natural and promising directions for Divooka is visual app development. Unlike traditional text-based languages, Divooka was built as a graphical, extensible environment from the start. Its own development environment &lt;em&gt;is&lt;/em&gt; a Divooka app - one that is, in principle, programmable with Divooka itself. That vision is still in blueprint form, and full realization will take time.&lt;/p&gt;
&lt;h3 id=&quot;business-collaboration&quot;&gt;Business Collaboration&lt;/h3&gt;
&lt;p&gt;From a business perspective, the experience of leading a team from late 2024 into early 2025 has been mixed. On the plus side, offloading certain tasks helped reduce some pressure. On the downside, with our current financial constraints, it's been hard to find help we can fully rely on - which, perhaps, is just reality.&lt;/p&gt;
&lt;p&gt;Teamwork through outsourcing introduced its own challenges. Choosing the right people is tough. Even highly capable partners don't always deliver the expected results. Interestingly, ChatGPT and other large language models played a strange but useful role in this phase - mainly by helping with research and code scaffolding.&lt;/p&gt;
&lt;p&gt;There's a pattern I've noticed: even when working with contractors, I end up defining the architecture and interfaces, and much of my time still goes into coordination. Once you're past the prototyping phase, iterations are limited, and results vary. In a similar way, large language models tend to produce &quot;one-shot&quot; outputs - either useful or not. But if I've already built the structure, the autocomplete results I get from an LLM often rival what a junior outsourced developer might provide.&lt;/p&gt;
&lt;p&gt;In that sense, I'd much rather work closely with people over time - collaborating, communicating, and refining a shared workflow. That's where real teamwork becomes meaningful.&lt;/p&gt;
&lt;h3 id=&quot;the-past&quot;&gt;The Past&lt;/h3&gt;
&lt;p&gt;Early on, development followed a pretty rigid schedule - when it was just me, with no meetings or other obligations, daily goals were clear and straightforward. But by mid-year, things were shifting constantly. On the one hand, that brought flexibility, and led to a number of unexpected developments, especially in product direction. On the other hand, such reactive planning made it harder to estimate timelines. A case in point: our &quot;first full version,&quot; originally scheduled for release this February, will likely be delayed until next year.&lt;/p&gt;
&lt;p&gt;But as mentioned earlier, even the definition of what qualifies as a &quot;first full version&quot; has evolved dramatically.&lt;/p&gt;
&lt;p&gt;In many ways, my approach to this company mirrors how I ran projects in university. First, I'm still the main driver and implementer. Second, I remain cautious when it comes to collaboration. Over the years, I've come to understand the &lt;em&gt;Economy of Scale&lt;/em&gt; in a deeply personal way - not in terms of wealth or headcount, but in how accumulated skills and better tools dramatically boost my individual productivity. In this age of AI, that effect is more visible than ever. Contrary to popular belief, &lt;em&gt;knowledge&lt;/em&gt; - not just capital - is the primary engine of productivity.&lt;/p&gt;
&lt;p&gt;That said, if you do have capital and a great team, collaborative scale can still achieve amazing things.&lt;/p&gt;
&lt;h3 id=&quot;entrepreneurship&quot;&gt;Entrepreneurship&lt;/h3&gt;
&lt;p&gt;One of the more surprising (though perhaps not shocking) aspects of this journey has been the mix of encouragement and skepticism I've received. Support has mostly come from peers in the professional world, while more traditional voices - old friends from university, family members - have tended to be more doubtful. Though, of course, there have been exceptions in both camps.&lt;/p&gt;
&lt;p&gt;This kind of reaction isn't often talked about on social media, but I think it's worth reflecting on.&lt;/p&gt;
&lt;p&gt;First, people naturally fear what they don't understand. Second, those closest to us - friends, family - end up becoming supporters in one way or another, whether they intend to or not. Third, when it comes to values and personal incentives, people respond differently to attempts to break convention - especially where money is involved.&lt;/p&gt;
&lt;p&gt;At a few conferences, I've had the chance to talk with other founders. Their motivations, strategies, and philosophies run the full spectrum. Some of the most frustrating folks are those who throw around the term &quot;AI&quot; without substance. (We too fell into that trap at one point, admittedly.) But these experiences - moments of confusion, attempts to find clarity - have opened up my view of the world far more than reading philosophy books or binge-watching TV shows ever did.&lt;/p&gt;
&lt;h3 id=&quot;the-future&quot;&gt;The Future&lt;/h3&gt;
&lt;p&gt;Looking ahead, I see three key challenges:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Finishing development and releasing the first full version&lt;/strong&gt; of the software.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Creating a complete educational system&lt;/strong&gt; around it, ensuring product coherence and knowledge accessibility.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Marketing and forming partnerships&lt;/strong&gt;, while keeping the company sustainable.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;2026 will be a demanding year. We'll need to maintain development speed while trimming back unnecessary administrative tasks. We want to move with precision, but not become overly cautious. The path forward requires boldness, care, grounded execution, and avoiding the lure of shortcuts. That's something I need to keep reminding myself.&lt;/p&gt;
</description>
<category>Career Roadmaps</category>
<guid isPermaLink="true">https://coderlegion.com/3838/one-year-anniversary-reflection-on-the-development-of-divooka</guid>
<pubDate>Tue, 08 Jul 2025 20:22:47 +0000</pubDate>
</item>
<item>
<title>DevLog 20250708: Procedural Context Visual Debugger in Divooka</title>
<link>https://coderlegion.com/3830/devlog-20250708-procedural-context-visual-debugger-in-divooka</link>
<description>&lt;h3 id=&quot;overview-10&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;How difficult is it to achieve Unreal Engine-style procedural context debugging?&lt;/p&gt;
&lt;p&gt;Turns out, it might be easier than expected.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/99z4fa3t53psaf6kzs0z.png&quot; alt=&quot;Unreal Debugging&quot;&gt;&lt;/p&gt;
&lt;p&gt;In this dev log, we won't implement all the fancy animations or full-featured GUI yet, but we &lt;em&gt;will&lt;/em&gt; derive a practical, functional debugger GUI. This interface allows users to step through a procedural program and observe its execution flow in real time.&lt;/p&gt;
&lt;h3 id=&quot;the-debugging-problem&quot;&gt;The Debugging Problem&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t1qcjwrsujpfy96tn2us.gif&quot; alt=&quot;Debugging Process&quot;&gt;&lt;/p&gt;
&lt;p&gt;Procedural contexts are hard - not necessarily to implement (they're actually simpler than dataflow contexts in interpretative runtimes, where you just step over nodes), but hard &lt;em&gt;for users&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The main issue is statefulness: once there's state, there's the potential for mistakes, side effects, and confusion.&lt;/p&gt;
&lt;p&gt;From an implementation standpoint, the hard part is debugging. Without visibility into execution, it's difficult to guess what's happening. That's why programming languages without IDEs often fall back on &lt;code&gt;Print&lt;/code&gt; statements to inspect runtime states. This approach quickly breaks down with larger programs or those involving intricate state transitions.&lt;/p&gt;
&lt;p&gt;Even Python - often used without an IDE - includes a built-in debugger in IDLE (&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.cs.uky.edu/~keen/help/debug-tutorial/debug.html&quot;&gt;see tutorial&lt;/a&gt;). It's essential when working with complex logic.&lt;/p&gt;
&lt;h3 id=&quot;approach&quot;&gt;Approach&lt;/h3&gt;
&lt;p&gt;Let's start by clarifying the setup: the current implementation of the Divooka graph editor (Neo) is done in WPF, using an interpretative runtime.&lt;/p&gt;
&lt;p&gt;For drawing the GUI, we can use standard XAML data bindings and a few button states:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xaml&quot;&gt;&amp;lt;StackPanel&amp;gt;
&amp;lt;!--Start--&amp;gt;
&amp;lt;Button Style=&quot;{StaticResource IconButtonStyle}&quot; Click=&quot;ProceduralContextRunGraphWithoutDebugging_Clicked&quot; ToolTip=&quot;Run and wait for the process to finish.&quot; Visibility=&quot;{Binding EvaluatingGraph, Converter={converters:BooleanToVisibilityConverter Negate=True}}&quot;/&amp;gt;

&amp;lt;!--Continue--&amp;gt;
&amp;lt;Button Style=&quot;{StaticResource DebugButtonStyle}&quot; Content=&quot;Continue&quot; Click=&quot;ProceduralContextContinueWithoutStepping_Clicked&quot; ToolTip=&quot;Skip debugging and continue the process to completion.&quot; Visibility=&quot;{Binding IsProceduralStepping, Converter={converters:BooleanToVisibilityConverter}}&quot;/&amp;gt;

&amp;lt;!--Step--&amp;gt;
&amp;lt;StackPanel Visibility=&quot;{Binding EvaluatingGraph, Converter={converters:BooleanToVisibilityConverter Negate=True}}&quot;&amp;gt;
&amp;lt;Button Style=&quot;{StaticResource DebugButtonStyle}&quot; Content=&quot;Step&quot; Click=&quot;ProceduralContextStartDebuggingGraph_Clicked&quot; ToolTip=&quot;Step into the process and observe each execution step.&quot; Visibility=&quot;{Binding IsProceduralStepping, Converter={converters:BooleanToVisibilityConverter Negate=True}}&quot;/&amp;gt;
&amp;lt;/StackPanel&amp;gt;

&amp;lt;!--Next Step--&amp;gt;
&amp;lt;Button Style=&quot;{StaticResource DebugButtonStyle}&quot; Content=&quot;Next&quot; Click=&quot;ProceduralContextContinueThroughGraph_Clicked&quot; ToolTip=&quot;Execute (step over) the next step in the process.&quot;  Visibility=&quot;{Binding IsProceduralStepping, Converter={converters:BooleanToVisibilityConverter}}&quot; IsEnabled=&quot;{Binding IsProceduralPaused}&quot;/&amp;gt;

&amp;lt;Button Style=&quot;{StaticResource DebugButtonStyle}&quot; Content=&quot;Finish&quot; Click=&quot;ProceduralContextFinishDebuggingGraph_Clicked&quot; ToolTip=&quot;Stop debugging the current process.&quot; Visibility=&quot;{Binding IsProceduralStepping, Converter={converters:BooleanToVisibilityConverter}}&quot;/&amp;gt;

&amp;lt;Button Style=&quot;{StaticResource DebugButtonStyle}&quot; Content=&quot;Pause&quot; Click=&quot;ProceduralContextPauseExecution_Clicked&quot; ToolTip=&quot;Pause execution of the current process.&quot; Visibility=&quot;{Binding EvaluatingGraph, Converter={converters:BooleanToVisibilityConverter}}&quot; IsEnabled=&quot;{Binding IsProceduralPaused, Converter={StaticResource InvertBooleanConverter}}&quot;/&amp;gt;
&amp;lt;/StackPanel&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;stepping-through-execution&quot;&gt;Stepping Through Execution&lt;/h4&gt;
&lt;p&gt;Initially, I thought we needed a dedicated execution runtime for stepping, due to the complexity of node interpretation. However, it turns out we can simply inject &quot;breakpoints&quot; during interpretation. The simplest way to achieve this in C# with multithreading is by using a &lt;code&gt;ManualResetEventSlim&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;// ── MainWindow.xaml.cs (WPF) ──
public partial class MainWindow : Window
{
// starts signaled (i.e. not paused)
    private readonly ManualResetEventSlim _pauseEvent = new ManualResetEventSlim(true);
    private readonly Executer _executer;

    public MainWindow()
    {
        InitializeComponent();
        _executer = new Executer(_pauseEvent);
    }

    private async void StartButton_Click(object sender, RoutedEventArgs e)
    {
// Run on thread-pool, but because we 'await', continuation
        // (Finish…) runs back on the UI thread.
        await Task.Run(() =&amp;gt; _executer.ExecuteGraph());
        FinishProceduralContextDebuggingSession();
    }

    private void PauseButton_Click(object sender, RoutedEventArgs e)
    {
// next time the worker hits Wait(), it will block
        _pauseEvent.Reset();
    }

    private void ResumeButton_Click(object sender, RoutedEventArgs e)
    {
// unblocks the worker
        _pauseEvent.Set();
    }
}

// ── Executer.cs ──
public class Executer
{
    private readonly ManualResetEventSlim _pauseEvent;
    private readonly Random _random = new Random();

    public Executer(ManualResetEventSlim pauseEvent)
    {
        _pauseEvent = pauseEvent;
    }

    public void ExecuteGraph()
    {
        while (/* your long-running condition */)
        {
            DoWorkStep();  

// simulate a random pause point
            if (_random.NextDouble() &amp;lt; 0.05)
            {
                Debug.WriteLine(&quot;…pausing now&quot;);
// put event into non-signaled (i.e. paused)
                _pauseEvent.Reset();
                _pauseEvent.Wait();  // block until resumed
                Debug.WriteLine(&quot;…resumed!&quot;);
            }
        }
    }

    private void DoWorkStep()
    {
        Thread.Sleep(200);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How It Works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;_pauseEvent&lt;/code&gt; starts in the signaled state, so &lt;code&gt;.Wait()&lt;/code&gt; returns immediately.&lt;/li&gt;
&lt;li&gt;Calling &lt;code&gt;_pauseEvent.Reset()&lt;/code&gt; switches it to non-signaled, so &lt;code&gt;.Wait()&lt;/code&gt; blocks the worker thread.&lt;/li&gt;
&lt;li&gt;When the UI calls &lt;code&gt;_pauseEvent.Set()&lt;/code&gt;, the blocked thread resumes execution.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;real-time-visual-updates&quot;&gt;Real-Time Visual Updates&lt;/h4&gt;
&lt;p&gt;A small challenge is real-time canvas updates - specifically, drawing a highlight box around the active node:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cc9wlvr9yv9ak29w575x.png&quot; alt=&quot;Highlight Box&quot;&gt;&lt;/p&gt;
&lt;p&gt;We can use an &lt;strong&gt;adorner&lt;/strong&gt; for this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;/// &amp;lt;summary&amp;gt;
/// Show a red highlight around the specified FrameworkElement for a set duration.
/// &amp;lt;/summary&amp;gt;
public static async void ShowSimpleHighlight(FrameworkElement target, double margin = 5, int duration = 3)
{
    if (target == null)
        return;

// Get the adorner layer for that target
    AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(target);
    if (adornerLayer == null)
        return;

// Create and add adorner to the layer
    HighlightAdorner highlightAdorner = new(target, margin);
    adornerLayer.Add(highlightAdorner);

// Remove it after a few seconds
    await Task.Delay(TimeSpan.FromSeconds(duration));
    adornerLayer.Remove(highlightAdorner);
}

public class HighlightAdorner : Adorner
{
    private readonly Pen _pen;
    private readonly double _margin;

    public HighlightAdorner(UIElement adornedElement, double margin = 5)
        : base(adornedElement)
    {
        _pen = new Pen(Brushes.Red, 2);
        _pen.Freeze();
        _margin = margin;
        IsHitTestVisible = false;
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        Rect adornedElementRect = new(AdornedElement.RenderSize);
        adornedElementRect.Inflate(_margin, _margin);
        drawingContext.DrawRectangle(null, _pen, adornedElementRect);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;what-s-next-&quot;&gt;What's Next?&lt;/h4&gt;
&lt;p&gt;All that's left is a proper debug window!&lt;/p&gt;
&lt;h3 id=&quot;summary-7&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;A good framework truly goes a long way. I had confidence in bringing procedural debugging to Divooka from the moment procedural context was introduced, because it naturally fits into an interpretative execution model. All that's required is a bit of focused engineering.&lt;/p&gt;
&lt;p&gt;The more challenging part is the GUI - but WPF offers solid isolation via MVVM, data binding, and adorners.&lt;/p&gt;
&lt;p&gt;We're not dealing with setting actual breakpoints yet, but the current setup is flexible enough to support that when the time comes. Ultimately, a debug window will act as our lens into the runtime: inspecting node states, variable values, and control flow.&lt;/p&gt;
&lt;h3 id=&quot;references-7&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Divooka/Language/Contexts&quot;&gt;Procedural vs Dataflow context&lt;/a&gt; in Divooka on the Methodox Wiki.&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.epicgames.com/documentation/en-us/unreal-engine/blueprint-debugging-example-in-unreal-engine&quot;&gt;Blueprint debugging in Unreal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Procedural debugging documentation on &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Divooka/Basic/ProceduralDebugging&quot;&gt;Methodox Wiki&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Divooka</category>
<guid isPermaLink="true">https://coderlegion.com/3830/devlog-20250708-procedural-context-visual-debugger-in-divooka</guid>
<pubDate>Tue, 08 Jul 2025 12:19:17 +0000</pubDate>
</item>
<item>
<title>DevLog 20250706: Speech to Text Transcription using OpenAI Whisper</title>
<link>https://coderlegion.com/3743/devlog-20250706-speech-to-text-transcription-using-openai-whisper</link>
<description>&lt;h3 id=&quot;overview-11&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;Mobile phones have had audio input for a long time, but none of the default options are particularly satisfactory. And despite the rise of capable online AI-based transcription services, for very simple scenarios like &quot;turn this recording into some text,&quot; there's still no easy tool.&lt;/p&gt;
&lt;p&gt;OpenAI released &lt;a rel=&quot;nofollow&quot; href=&quot;https://openai.com/index/whisper/&quot;&gt;Whisper in 2022&lt;/a&gt;, a powerful model capable of transcribing many languages - but even now, there's no straightforward way to use it without invoking the API directly.&lt;/p&gt;
&lt;h3 id=&quot;the-api-1&quot;&gt;The API&lt;/h3&gt;
&lt;p&gt;Under the hood, Whisper is a deep neural network trained end-to-end to map raw audio to text. Conceptually, you:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Provide an audio input&lt;/strong&gt; - the model analyzes the waveform to extract linguistic and acoustic features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leverage learned representations&lt;/strong&gt; - its multi-layer architecture handles background noise, varied accents, and low-quality recordings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Produce a transcription&lt;/strong&gt; - Whisper outputs a sequence of text that you can display, store, or post-process.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This high-level interaction keeps things simple: feed in speech, get back text - no need to manage model internals or low-level signal processing.&lt;/p&gt;
&lt;h3 id=&quot;the-utility&quot;&gt;The Utility&lt;/h3&gt;
&lt;p&gt;Today I'm sharing our free &lt;strong&gt;Transcriber&lt;/strong&gt; tool, which I've been using for almost half a year. It does a solid job at what it's meant to do:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox.itch.io/transcriber&quot;&gt;https://methodox.itch.io/transcriber&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We likely won't have time to develop it further, but sharing it online makes it more accessible for others looking for a similar solution.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q249ty0unthyy0eummh4.png&quot; alt=&quot;Screenshot&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;challenges&quot;&gt;Challenges&lt;/h3&gt;
&lt;p&gt;Currently, there's a limit on audio length due to OpenAI API restrictions. It would also be ideal to add real-time transcription - something like Google Voice IME.&lt;/p&gt;
&lt;h3 id=&quot;references-8&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Utility download: &lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox.itch.io/transcriber&quot;&gt;Transcriber&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>API Dev</category>
<guid isPermaLink="true">https://coderlegion.com/3743/devlog-20250706-speech-to-text-transcription-using-openai-whisper</guid>
<pubDate>Mon, 07 Jul 2025 02:08:17 +0000</pubDate>
</item>
<item>
<title>DevLog 20250706: Analyzing (C#) Project Dependencies</title>
<link>https://coderlegion.com/3738/devlog-20250706-analyzing-c-project-dependencies</link>
<description>&lt;p&gt;When a code project grows complex it's going to be difficult to understand which depends on which - developers familiar with the C# ecosystem can look at NuGet Package Manager and use &lt;code&gt;.csproj&lt;/code&gt; to try to study things but when the hierarchy gets complex it can feel inconvenient.&lt;/p&gt;
&lt;p&gt;For instance, say I have a top-level entry program that I know ultimately depends on &lt;code&gt;Mathnet.Numerics&lt;/code&gt; - now I need to figure out which subproject references it.&lt;/p&gt;
&lt;h3 id=&quot;the-general-approach&quot;&gt;The General Approach&lt;/h3&gt;
&lt;p&gt;To view which project depend on which package in C#, below are some approaches using existing tools.&lt;/p&gt;
&lt;h4 id=&quot;1-using-the-net-cli&quot;&gt;1. Using the .NET CLI&lt;/h4&gt;
&lt;p&gt;This approach only shows the NuGet packages the projects depend on.&lt;/p&gt;
&lt;h5 id=&quot;a-list-packages-per-project&quot;&gt;a) List packages per project&lt;/h5&gt;
&lt;p&gt;Run, for each project:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;dotnet list path/to/MyProject.csproj package
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We'll get an &lt;em&gt;&quot;Installed&quot;&lt;/em&gt; section showing direct references.&lt;/p&gt;
&lt;h5 id=&quot;b-include-transitive-dependencies&quot;&gt;b) Include transitive dependencies&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;dotnet list path/to/MyProject.csproj package --include-transitive
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will also show packages pulled in by other packages.&lt;/p&gt;
&lt;h5 id=&quot;c-script-it-for-the-entire-solution&quot;&gt;c) Script it for the entire solution&lt;/h5&gt;
&lt;p&gt;On &lt;strong&gt;bash&lt;/strong&gt; (Linux/macOS/git-bash):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;for proj in $(find . -name '*.csproj'); do
  echo &quot;== $proj ==&quot;  
  dotnet list &quot;$proj&quot; package --include-transitive | grep MathNet.Numerics -B2 -A2
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On &lt;strong&gt;PowerShell&lt;/strong&gt; (Windows):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Get-ChildItem -Recurse -Filter *.csproj |
  ForEach-Object {
    Write-Host &quot;== $($_.FullName) ==&quot;
    dotnet list $_.FullName package --include-transitive |
      Select-String &quot;MathNet.Numerics&quot; -Context 2,2
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-grep-select-string-on-csproj-files&quot;&gt;2. Grep / Select-String on &lt;code&gt;.csproj&lt;/code&gt; files&lt;/h4&gt;
&lt;p&gt;Since NuGet package references live in &lt;code&gt;&amp;lt;PackageReference&amp;gt;&lt;/code&gt; items, we can search directly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;bash&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;grep -R &quot;PackageReference.*MathNet\.Numerics&quot; -n .
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PowerShell&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Get-ChildItem -Path . -Filter *.csproj -Recurse | Select-String -Pattern 'MathNet\.Numerics' -List
  
# Or
Get-ChildItem -Recurse -Filter *.csproj | Select-String -SimpleMatch 'MathNet.Numerics' -List
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That will show us exactly which project files reference it.&lt;/p&gt;
&lt;p&gt;Alternatively, just do a full text search in VS Code with file type filter.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z8u46rq4ltd19lj807dg.png&quot; alt=&quot;Search in VS Code&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;3-visual-studio-rider-ui&quot;&gt;3. Visual Studio / Rider UI&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Right-click&lt;/strong&gt; on the &lt;strong&gt;Solution&lt;/strong&gt; → &lt;strong&gt;Manage NuGet Packages for Solution…&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Go to the &lt;strong&gt;Installed&lt;/strong&gt; tab, find &lt;strong&gt;MathNet.Numerics&lt;/strong&gt; in the left panel.&lt;/li&gt;
&lt;li&gt;On the right, we'll see checkboxes or project-scoped lists showing which projects have it installed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once again, this method doesn't show the complete picture or the hierarchy of dependencies.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6jjn66fa19sld80ymobr.png&quot; alt=&quot;Manage NuGets for Entire Solution&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;4-msbuild-dependency-graph-advanced-&quot;&gt;4. MSBuild Dependency Graph (advanced)&lt;/h4&gt;
&lt;p&gt;If we need to audit &lt;strong&gt;transitive&lt;/strong&gt; and &lt;strong&gt;project-to-project&lt;/strong&gt; dependencies in one go, we can generate a restore graph:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;dotnet msbuild /t:GenerateRestoreGraphFile /p:RestoreGraphOutputPath=graph.dg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This produce a fairly comprehensive output that contains absolute paths and a bit too much irrelevant information.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qzkdw17u1njkamxsekus.png&quot; alt=&quot;graph.dh&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then inspect &lt;code&gt;graph.dg&lt;/code&gt; (JSON) for entries where &lt;code&gt;&quot;PackageId&quot;:&quot;MathNet.Numerics&quot;&lt;/code&gt; and note the &lt;code&gt;&quot;ProjectPath&quot;&lt;/code&gt; fields.&lt;/p&gt;
&lt;h3 id=&quot;custom-tool&quot;&gt;Custom Tool&lt;/h3&gt;
&lt;p&gt;Above methods work but if you don't like the output format, CLI syntax, or just want something more straightforward - then it's time for a custom tool.&lt;/p&gt;
&lt;p&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MethodoxTech/Mirror_Tool_CSharpDependencyAnalyzer&quot;&gt;C# Dependency Analyzer&lt;/a&gt; is a tool that can quickly help identify dependencies of a source code project. This is useful in identifying unwanted nuget dependencies from any given entry assembly. It is equivalent to manually searching through .csproj to find which projects depend on which and debug NuGet dependencies.&lt;/p&gt;
&lt;p&gt;It can do those three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Print an exhaustive dependency tree with a root node at each source project level down to all dependent projects and most importantly the root NuGets&lt;/li&gt;
&lt;li&gt;Given a single target project/nuget, prints a filtered tree of all projects that depend on that assembly&lt;/li&gt;
&lt;li&gt;Given a single target project/nuget and a source assembly, it prints the tree paths that lead from the source assembly to the target - showing clearly the path from the source assembly to the ultimate dependency.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Below is an example output with command: &lt;code&gt;path --path &amp;lt;solution path&amp;gt; --source Transcriber --target SkiaSharp&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/phyw6tk6u14u30hit0l6.png&quot; alt=&quot;Dependency look up&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;references-9&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Methodox Dependency analyzer: visit &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MethodoxTech/Mirror_Tool_CSharpDependencyAnalyzer&quot;&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>C#</category>
<guid isPermaLink="true">https://coderlegion.com/3738/devlog-20250706-analyzing-c-project-dependencies</guid>
<pubDate>Sun, 06 Jul 2025 19:26:37 +0000</pubDate>
</item>
<item>
<title>Data-Driven Design: Leveraging Lessons from Game Development in Everyday Software</title>
<link>https://coderlegion.com/3459/data-driven-design-leveraging-lessons-from-game-development-in-everyday-software</link>
<description>&lt;h3 id=&quot;overview-12&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;Modern software often needs to adapt quickly - whether that means processing new data sets, adjusting to user preferences, or deploying new features safely without downtime. To achieve such flexibility, software engineers increasingly adopt a methodology known as &lt;strong&gt;Data-Driven Design (DDD)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Originally popularized by game development, Data-Driven Design emerged prominently in the 1990s as large studios confronted a challenging problem: the need to iterate rapidly on complex and interactive content. Game developers realized it was costly and slow to rebuild and redeploy an entire game every time designers wanted to tweak gameplay mechanics, adjust character behaviors, or revise in-game dialogues.&lt;/p&gt;
&lt;p&gt;Jason Gregory's influential book &lt;em&gt;Game Engine Architecture&lt;/em&gt; highlighted how AAA games effectively tackled this challenge by externalizing game logic into structured &lt;strong&gt;data files&lt;/strong&gt;. Instead of embedding behaviors directly in C++ code, developers loaded data such as AI rules, game levels, item descriptions, and story dialogues from easily editable files like YAML, JSON, or custom formats. This dramatically accelerated iteration, empowering non-programmers - artists, designers, and writers - to directly experiment and refine experiences without requiring code recompilation or redeployment.&lt;/p&gt;
&lt;p&gt;Although originally rooted in game development, Data-Driven Design has proven invaluable across software domains, ranging from web development, data analytics, and DevOps automation to no-code and low-code platforms. The fundamental principle remains the same: separate &lt;strong&gt;generic engines&lt;/strong&gt; from &lt;strong&gt;domain-specific data&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-core-idea-behind-data-driven-design&quot;&gt;The Core Idea Behind Data-Driven Design&lt;/h3&gt;
&lt;p&gt;Before we dive deeper, let's provide a clear working definition that illustrates why Data-Driven Design is relevant to developers and system administrators today:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Data-Driven Design&lt;/strong&gt; means that the software's behavior is governed by external data rather than hard-coded logic. The code provides generic mechanisms for processing that data, but the specifics of “what to do” or “how to behave” live in data files (such as YAML, JSON, or databases) that can be changed independently from the source code itself.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To clarify further, here's a quick comparison:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;strong&gt;Pattern&lt;/strong&gt;       &lt;/th&gt;&lt;th&gt;&lt;strong&gt;What drives behavior?&lt;/strong&gt;                                        &lt;/th&gt;&lt;th&gt;&lt;strong&gt;Practical Example&lt;/strong&gt;&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Hard-coded&lt;/strong&gt;    &lt;/td&gt;&lt;td&gt;Embedded conditional statements in code                          &lt;/td&gt;&lt;td&gt;&lt;code&gt;if user.is_premium: enable_feature()&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Config-driven&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Simple flags or settings in config files                         &lt;/td&gt;&lt;td&gt;&lt;code&gt;max_connections = 10&lt;/code&gt; in &lt;code&gt;config.ini&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Data-driven&lt;/strong&gt;   &lt;/td&gt;&lt;td&gt;Entire behaviors defined in structured data (YAML, JSON, SQLite) &lt;/td&gt;&lt;td&gt;YAML defining a workflow or SQLite storing business rules&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Below are some common misconceptions and anti-patterns to help clarity things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&quot;It's just a config file.&quot;&lt;/strong&gt; – If removing the file breaks the program, it's &lt;em&gt;not&lt;/em&gt; mere config; it's &lt;em&gt;content&lt;/em&gt; that &lt;em&gt;defines&lt;/em&gt; runtime behaviour.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&quot;DDD = No code.&quot;&lt;/strong&gt; – Wrong: the goal is to keep the &lt;em&gt;engine&lt;/em&gt; generic and thin, but domain logic still has to live &lt;em&gt;somewhere&lt;/em&gt; (often in a domain-specific language interpreted by the engine).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Premature complexity&lt;/strong&gt; –  Don't invent a custom DSL when a handful of YAML documents plus a small interpreter class will do.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Debugging blind&lt;/strong&gt; –  Always log &quot;which data row caused this action?&quot; so you can trace bugs quickly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;high-level-architecture-overview&quot;&gt;High-Level Architecture Overview&lt;/h3&gt;
&lt;p&gt;A typical Data-Driven Design architecture clearly separates:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Authoring Layer&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Human-readable formats like YAML or JSON.&lt;/li&gt;
&lt;li&gt;Editable directly by users or via automated processes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validation &amp;amp; Build Layer&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Schema definitions or migrations that ensure data consistency.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Runtime Loader &amp;amp; Hot Reload&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reads and validates data at runtime.&lt;/li&gt;
&lt;li&gt;Supports hot-reload for rapid iterations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generic Runtime System&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Executes logic based purely on loaded data.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This creates a robust pipeline where data edits alone trigger different application behaviors.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Data Files (YAML/SQLite)
    │ load &amp;amp; validate
    ▼
Generic Runtime Engine
    │ executes behaviors based on data
    ▼
Dynamic Behavior in Application
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;choosing-your-data-container&quot;&gt;Choosing Your Data Container&lt;/h3&gt;
&lt;p&gt;Here's a quick guide to choosing your data format based on needs:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;strong&gt;Format&lt;/strong&gt;         &lt;/th&gt;&lt;th&gt;&lt;strong&gt;Advantages&lt;/strong&gt;                               &lt;/th&gt;&lt;th&gt;&lt;strong&gt;Typical Use Case&lt;/strong&gt;&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;YAML/JSON/TOML&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Human-readable, simple to version control    &lt;/td&gt;&lt;td&gt;Small-to-medium complexity workflows, configs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;SQLite&lt;/strong&gt;         &lt;/td&gt;&lt;td&gt;Relational queries, consistency, single file &lt;/td&gt;&lt;td&gt;Complex rule sets, relational data, analytics&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Binary Formats&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Performance-critical loading                 &lt;/td&gt;&lt;td&gt;Embedded systems, high-performance scenarios&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Typically, you start with YAML or JSON, then upgrade to SQLite as complexity grows.&lt;/p&gt;
&lt;h3 id=&quot;practical-example-data-driven-workflows-with-divooka&quot;&gt;Practical Example: Data-Driven Workflows with Divooka&lt;/h3&gt;
&lt;p&gt;Divooka is the visual programming language that naturally embodies Data-Driven Design principles through its composable dataflow nodes. Let's illustrate this clearly with a straightforward example:&lt;/p&gt;
&lt;h4 id=&quot;scenario-&quot;&gt;Scenario:&lt;/h4&gt;
&lt;p&gt;A data analyst wants to build an automated workflow to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Load a CSV file containing customer orders.&lt;/li&gt;
&lt;li&gt;Filter orders exceeding a certain threshold.&lt;/li&gt;
&lt;li&gt;Send a summary via a web API.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In Divooka, the process is streamlined as follows:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Load Data with Path and File Nodes&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Path&lt;/strong&gt; node specifies file location (e.g., &lt;code&gt;/data/orders.csv&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Load from CSV&lt;/strong&gt; file read node directly loads CSV data into a structured &lt;strong&gt;DataGrid&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;[Path Node] &quot;/data/orders.csv&quot;
    │
    └─► [Load from CSV Node]
           │
           └─► DataGrid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Equivalent Python-like pseudocode:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;orders_df = pd.read_csv(&quot;/data/orders.csv&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. Filtering Data&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; node takes the DataGrid and filters based on condition (e.g., &lt;code&gt;order_total &amp;gt; 100&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;DataGrid
    │
    └─► [Filter Node] condition: order_total &amp;gt; 100
           │
           └─► Filtered DataGrid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Equivalent pseudocode:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;high_value_orders = orders_df[orders_df[&quot;order_total&quot;] &amp;gt; 100]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. Sending Data via HTTP API&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;String&lt;/strong&gt; node specifies the webhook URI.&lt;/li&gt;
&lt;li&gt;HTTP &lt;strong&gt;Send Request&lt;/strong&gt; node sends the filtered data automatically serialized as JSON.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;[String Node] &quot;https://hooks.example.com/orders&quot;
    │
    └─► [Send Request Node] method: POST, body: Filtered DataGrid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Equivalent pseudocode:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import requests
payload = high_value_orders.to_dict(orient='records')
requests.post(&quot;https://hooks.example.com/orders&quot;, json=payload)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;why-divooka-is-naturally-data-driven-&quot;&gt;Why Divooka Is Naturally Data-Driven:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High-level abstraction nodes&lt;/strong&gt; (Path, String, File I/O, Web Request) handle data transparently.&lt;/li&gt;
&lt;li&gt;Nodes clearly define dependencies and flow, automatically adjusting behavior when input data changes.&lt;/li&gt;
&lt;li&gt;Entire workflows can be modified without touching underlying engine code or restarting the runtime environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;best-practices-and-pitfalls&quot;&gt;Best Practices and Pitfalls&lt;/h3&gt;
&lt;p&gt;When embracing Data-Driven Design, keep the following in mind:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;strong&gt;Practice&lt;/strong&gt;                 &lt;/th&gt;&lt;th&gt;&lt;strong&gt;Recommendation&lt;/strong&gt;&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Schema Validation&lt;/strong&gt;        &lt;/td&gt;&lt;td&gt;Always enforce schemas (e.g., JSON Schema or SQLite migrations) to prevent errors&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Hot Reloading&lt;/strong&gt;            &lt;/td&gt;&lt;td&gt;Clearly separate parsing from data activation steps&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Security Considerations&lt;/strong&gt;  &lt;/td&gt;&lt;td&gt;Never trust external data implicitly; always validate or sanitize it before use&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Performance Optimization&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Cache loaded data where appropriate to avoid redundant parsing&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;try-it-yourself-starter-projects&quot;&gt;Try It Yourself: Starter Projects&lt;/h3&gt;
&lt;p&gt;To practice Data-Driven Design, consider these beginner-friendly projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Feature Flag Dashboard&lt;/strong&gt;: YAML-based flags control feature visibility.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Email Routing Automation&lt;/strong&gt;: SQLite stores routing rules to sort incoming emails.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Procedural Content Generator&lt;/strong&gt;: YAML files configure parameters for generated data outputs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each project reinforces that your engine stays generic and data explicitly drives all behaviors.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-7&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Data-Driven Design originated as a powerful solution to rapid iteration in the game development world, enabling large teams to build and adjust complex software efficiently. Today, its principles transcend industries, benefiting system administrators, data analysts, web developers, and more.&lt;/p&gt;
&lt;p&gt;In tools like Divooka, Data-Driven Design emerges naturally - dataflow-based nodes inherently separate behavior from implementation, ensuring flexibility and robustness. By clearly distinguishing between generic program logic and externally-driven data, you create maintainable, adaptable, and resilient software.&lt;/p&gt;
&lt;h3 id=&quot;references-10&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Wikipedia on &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Data-driven_programming&quot;&gt;Data-driven programming&lt;/a&gt; - not to be confused with &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Data-oriented_design&quot;&gt;Data-oriented design&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Jason Gregory's book on &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.gameenginebook.com/toc.html&quot;&gt;Game Engine Architecture (table of contents)&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Richard Fabian's book on &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.dataorienteddesign.com/dodbook/node2.html&quot;&gt;Data-Oriented Design (online version)&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See also:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Knowledge base on &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/KnowledgeBase/SQL&quot;&gt;SQL&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/KnowledgeBase/SQLite&quot;&gt;SQLite&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Divooka</category>
<guid isPermaLink="true">https://coderlegion.com/3459/data-driven-design-leveraging-lessons-from-game-development-in-everyday-software</guid>
<pubDate>Tue, 24 Jun 2025 22:18:04 +0000</pubDate>
</item>
<item>
<title>SQLite - High-Level Introduction</title>
<link>https://coderlegion.com/3458/sqlite-high-level-introduction</link>
<description>&lt;h2 id=&quot;sqlite&quot;&gt;SQLite&lt;/h2&gt;
&lt;p&gt;SQLite is a self-contained, serverless, zero-configuration relational database engine. Unlike traditional database servers, SQLite is distributed as a simple library that you embed directly into your applications. This guide will introduce you to SQLite's core concepts, explain why it's such a popular choice for developers, highlight how it differs from other database systems, and show how you can leverage it inside Divooka's visual programming environment.&lt;/p&gt;
&lt;h3 id=&quot;why-sqlite-&quot;&gt;Why SQLite?&lt;/h3&gt;
&lt;p&gt;For many new programmers, setting up a client–server database (e.g., MySQL, PostgreSQL) can feel like learning two things at once: programming &lt;strong&gt;and&lt;/strong&gt; system administration. SQLite removes that overhead:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zero-configuration&lt;/strong&gt;&lt;br&gt;
No daemon to install or manage - your application just opens a file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lightweight and portable&lt;/strong&gt;&lt;br&gt;
Entire database lives in a single cross-platform file (often just a few hundred kilobytes of library code).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Broad language support&lt;/strong&gt;&lt;br&gt;
Bindings exist for Python, JavaScript, C#, Java, Go, Rust, and more - so you can start querying data in the language you already know.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because of its simplicity, SQLite is the most widely deployed database engine in the world, powering everything from mobile apps and desktop software to IoT devices and web browsers.&lt;/p&gt;
&lt;h3 id=&quot;core-architecture&quot;&gt;Core Architecture&lt;/h3&gt;
&lt;h4 id=&quot;file-based-storage&quot;&gt;File-Based Storage&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Single file&lt;/strong&gt;: Definitions, tables, indexes, and data all live in one &lt;code&gt;.db&lt;/code&gt; (or &lt;code&gt;.sqlite3&lt;/code&gt;) file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-platform&lt;/strong&gt;: You can copy the database file between Windows, macOS, Linux, and mobile devices without conversion.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The file-based nature of SQLite makes it super easy to backup and migrate databases.&lt;/p&gt;
&lt;h4 id=&quot;serverless-and-embedded&quot;&gt;Serverless and Embedded&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Library, not a server&lt;/strong&gt;: Applications link against &lt;code&gt;libsqlite3&lt;/code&gt; and call C-APIs (or higher-level wrappers) directly - there's no separate &quot;database process.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reduced latency&lt;/strong&gt;: Reads and writes happen in-process, avoiding inter-process communication overhead found in client–server setups.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;dynamic-typing-manifest-typing-&quot;&gt;Dynamic Typing (&quot;Manifest Typing&quot;)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Types on values, not columns&lt;/strong&gt;: Columns have &quot;type affinities&quot; (e.g., &lt;code&gt;INTEGER&lt;/code&gt;, &lt;code&gt;TEXT&lt;/code&gt;), but SQLite won't reject a string inserted into an &lt;code&gt;INTEGER&lt;/code&gt; column - it will try to convert it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optional strict mode&lt;/strong&gt;: As of recent versions, you can declare &lt;code&gt;CREATE TABLE ... STRICT&lt;/code&gt; to enforce traditional SQL type checking if you need it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;concurrency-model&quot;&gt;Concurrency Model&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reader–writer locks&lt;/strong&gt;: Multiple readers can operate simultaneously, but writes acquire an exclusive lock on the file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Write-Ahead Logging (WAL)&lt;/strong&gt;: Enables concurrent reads and writes by appending changes to a separate log, improving throughput for mixed workloads.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;key-differences-from-other-databases&quot;&gt;Key Differences from Other Databases&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Aspect               &lt;/th&gt;&lt;th&gt;SQLite                                  &lt;/th&gt;&lt;th&gt;Client–Server Databases&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Deployment           &lt;/td&gt;&lt;td&gt;Embed a library; open a local file      &lt;/td&gt;&lt;td&gt;Install/maintain a separate server&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Configuration        &lt;/td&gt;&lt;td&gt;Zero-config; managed by file system     &lt;/td&gt;&lt;td&gt;Requires user accounts, ports, etc.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Process architecture &lt;/td&gt;&lt;td&gt;In-process library calls                &lt;/td&gt;&lt;td&gt;Inter-process with network protocol&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Typing               &lt;/td&gt;&lt;td&gt;Dynamic, manifest typing                &lt;/td&gt;&lt;td&gt;Static, enforced column types&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Concurrency          &lt;/td&gt;&lt;td&gt;Serialized writes (with WAL option)     &lt;/td&gt;&lt;td&gt;True concurrent writes via server&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Backup               &lt;/td&gt;&lt;td&gt;Copy the file or use online backup APIs &lt;/td&gt;&lt;td&gt;Use dump utilities or replication&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;practical-use-cases&quot;&gt;Practical Use Cases&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Desktop and Mobile Apps&lt;/strong&gt;&lt;br&gt;
Store user settings, preferences, and offline data without bundling a full database server.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Embedded and IoT Devices&lt;/strong&gt;&lt;br&gt;
Firmware and edge-computing modules often rely on SQLite for local data persistence, thanks to its tiny footprint.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prototyping and Testing&lt;/strong&gt;&lt;br&gt;
Quickly spin up a relational store for experiments and unit tests - no setup scripts needed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;High-Availability Reads&lt;/strong&gt;&lt;br&gt;
Applications that require many concurrent read operations (e.g., analytics dashboards) benefit from SQLite's low-latency access.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;File-Based Data Interchange&lt;/strong&gt;&lt;br&gt;
Sharing a &lt;code&gt;.db&lt;/code&gt; file between teams or tools (e.g., GIS software, data science notebooks) is as simple as sharing a CSV.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;sqlite-in-divooka&quot;&gt;SQLite in Divooka&lt;/h3&gt;
&lt;p&gt;In Divooka's visual programming language, SQLite plays two roles:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;In-Memory Data Engine&lt;/strong&gt;: Run queries against data you're manipulating on the canvas - think temporary tables without disk I/O.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Persistent Storage&lt;/strong&gt;: Divooka Explore ships with SQLite pre-installed, so you can save and load your data without any extra configuration - just specifying a filepath for the database and you are good to go.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simple Web Interface&lt;/strong&gt;: The O'ista web framework offers a capability for you to host SQLite online with authentication, so the database can be accessed by others.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This integration means you can drag a &quot;SQL Query&quot; node onto your workflow, point it at either an in-memory dataset or a saved &lt;code&gt;.db&lt;/code&gt; file, and immediately start filtering, aggregating, and joining tables - all within Divooka's visual interface.&lt;/p&gt;
&lt;h3 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open/Create a database&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- In the sqlite3 shell:
.open my_project.db
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create tables&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE users (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  joined DATE
);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Insert sample data&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;INSERT INTO users (name, joined)
VALUES ('Alice', '2025-06-01'),
       ('Bob',   '2025-06-15');
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Run a query&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT id, name
FROM users
WHERE joined &amp;gt;= '2025-06-10';
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In Divooka Explore, these same SQL snippets can be entered into a &lt;strong&gt;SQL&lt;/strong&gt; query block node, letting you mix SQL with visual data flows.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-8&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;SQLite offers a frictionless path for beginners to learn SQL and build data-driven applications, while still providing advanced features like ACID transactions, full-text search, and JSON support for more experienced developers. Its unique combination of simplicity, portability, and performance makes it an ideal companion for both standalone projects and integrations inside environments like Divooka. Whether you're just exploring databases for the first time or architecting an embedded system, SQLite is a tool that scales with your needs - no server required.&lt;/p&gt;
&lt;h3 id=&quot;references-11&quot;&gt;References&lt;/h3&gt;
&lt;p&gt;See also:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SQL refresher on Methodox Wiki  &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/KnowledgeBase/SQL&quot;&gt;Knowledge Base&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;O'ista web framework &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io//Frameworks/Oista/Capabilities&quot;&gt;capabilities&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;SQLite official &lt;a rel=&quot;nofollow&quot; href=&quot;https://sqlite.org/faq.html&quot;&gt;FAQ&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Backend Dev</category>
<guid isPermaLink="true">https://coderlegion.com/3458/sqlite-high-level-introduction</guid>
<pubDate>Tue, 24 Jun 2025 15:27:25 +0000</pubDate>
</item>
<item>
<title>Computing in the Financial Sector: An Ideal Scenario</title>
<link>https://coderlegion.com/3378/computing-in-the-financial-sector-an-ideal-scenario</link>
<description>&lt;blockquote&gt;&lt;p&gt;Technical debt is like refusing to fix a leaking roof because it's raining - only to forget about it when the sun comes out.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this article, I'd like to share some thoughts on computing in professional environments, especially from an engineering perspective. These are based on over 18 years of experience in software and insights gathered from three years of observing workflows and speaking with veterans across industries - financial services, banking, retail, and engineering alike.&lt;/p&gt;
&lt;p&gt;The financial industry is used here as an example - not because it is uniquely flawed, but because it is highly relatable and emblematic of issues in many complex corporate environments. The emphasis here is on &lt;strong&gt;computing infrastructure&lt;/strong&gt;, though the discussion touches on &lt;strong&gt;organizational and human factors&lt;/strong&gt;, which are often inseparable in real-world implementations.&lt;/p&gt;
&lt;p&gt;As an engineer, I strive for &lt;strong&gt;system coherence and comprehensibility&lt;/strong&gt;. Complexity is natural and even necessary. &lt;strong&gt;Complication, however, is a human failure - and preventable.&lt;/strong&gt; This piece is not about client-facing technologies, but about the &lt;strong&gt;internal systems, processes, and philosophies&lt;/strong&gt; that make a corporation function, scale, and evolve.&lt;/p&gt;
&lt;h3 id=&quot;summary-8&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;This article is long, so here's the TL;DR:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Avoid most commercial cloud services&lt;/strong&gt; unless absolutely necessary.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Minimize hierarchies.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use Azure DevOps (ADO)&lt;/strong&gt; for task management (there is no equivalent that offers the same integration).&lt;/li&gt;
&lt;li&gt;Provide &lt;strong&gt;scriptable, reusable interfaces&lt;/strong&gt; for historical analysis and reporting (e.g., Grafana, TradeView).&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;SQL (raw or wrapped)&lt;/strong&gt; throughout your stack for ETL.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avoid PowerBI&lt;/strong&gt; for anything mission-critical.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Never use Excel&lt;/strong&gt; in production.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Invest in tailored tools&lt;/strong&gt; for core business functions (e.g., custom accounting platforms over QuickBooks).&lt;/li&gt;
&lt;li&gt;Consider &lt;strong&gt;interactive simulations or serious games&lt;/strong&gt; for training.&lt;/li&gt;
&lt;li&gt;Design your enterprise as &lt;strong&gt;API-first&lt;/strong&gt;, not &quot;AI-first&quot; (AI-first is often just a buzzword).&lt;/li&gt;
&lt;li&gt;Prefer &lt;strong&gt;locally deployed AI models&lt;/strong&gt;, ideally under your control.&lt;/li&gt;
&lt;li&gt;Underpinning all of this is a core belief: &lt;strong&gt;existing practices are broken&lt;/strong&gt;, and a new paradigm is needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While &lt;em&gt;Divooka&lt;/em&gt; (our own platform) embodies this philosophy, this is not an advertisement. Any platform that addresses the same core issues is welcome - though in years of searching, I've yet to find one that does.&lt;/p&gt;
&lt;h3 id=&quot;motivation-1&quot;&gt;Motivation&lt;/h3&gt;
&lt;p&gt;For both individuals and teams, the drive for faster workflows and better tools is ultimately about &lt;strong&gt;insight&lt;/strong&gt;. In complex business environments, speed enables iteration, and iteration enables clarity. This is just as true for corporations as it is for individuals. But iteration only works if &lt;strong&gt;tools are adaptable&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Hence, the &lt;strong&gt;efficiency of problem-solving is directly tied to the efficiency of developing custom internal tools.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For large institutions - national pension funds, tier-one banks, or multi-national insurance firms - this goes beyond tooling. You need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Layered access control&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;High-frequency analysis&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Training pathways for new hires (e.g., interns)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More importantly, &lt;strong&gt;data accessibility&lt;/strong&gt; - real-time, API-based, version-controlled - is what drives real analytical agility. &lt;em&gt;Security is not just a technical problem - it is a cultural and process problem.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Top-down visibility is also essential. Leadership should be able to trace any issue through systems with a single click. If they can't, &lt;strong&gt;the system has failed&lt;/strong&gt; - not because of complexity, but because of poor design.&lt;/p&gt;
&lt;p&gt;And while executives don't need to understand every implementation detail, they must understand the &lt;strong&gt;principles&lt;/strong&gt; - and more critically, &lt;strong&gt;invest in education&lt;/strong&gt; both for themselves and for entry-level employees. A longer-term view builds resilience, retains talent, and enables innovation.&lt;/p&gt;
&lt;h3 id=&quot;making-the-best-of-existing-tools&quot;&gt;Making the Best of Existing Tools&lt;/h3&gt;
&lt;p&gt;Cloud computing has made off-the-shelf infrastructure appealing - but this convenience comes at a cost. Over time, the lack of control becomes a liability, especially at scale. High-performing organizations must eventually &lt;strong&gt;invest in internal capabilities&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;It is always cheaper in the long run to take matters into your own hands - if you can attract and retain technical talent.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Additionally, &lt;strong&gt;code review must be mandatory&lt;/strong&gt; for anything related to automation - whether for internal dashboards, accounting tools, or production pipelines. Not just for QA, but for education and sustainability.&lt;/p&gt;
&lt;p&gt;In software development cycles, a healthy time division might look like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1.5x Analysis/Modeling&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;1x Development&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0.5x Code Review&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0.5x QA&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Financial organizations should also standardize on &lt;strong&gt;microservices&lt;/strong&gt; to simplify endpoint access and reduce cross-system coupling.&lt;/p&gt;
&lt;p&gt;For any company that uses the word &quot;agile&quot; unironically: &lt;strong&gt;Azure DevOps (ADO)&lt;/strong&gt; remains the gold standard for work item tracking, integrated version control, testing, and pipeline automation.&lt;/p&gt;
&lt;p&gt;A critical warning: &lt;strong&gt;Beware the proliferation of internal dashboards.&lt;/strong&gt; While it may seem counterintuitive, the tendency to build highly visual dashboards is a distraction from what matters - &lt;strong&gt;reliable APIs, scalable infrastructure, and actual automation&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;&quot;API-first&quot; saves 80% of the effort in the long term compared to building UI-first.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;the-peer-developer-model&quot;&gt;The Peer Developer Model&lt;/h3&gt;
&lt;p&gt;Financial analysts are de facto computing professionals. Any work at scale is computational. And yet, most organizations underinvest in bridging the gap between analysis and implementation.&lt;/p&gt;
&lt;p&gt;There are two solutions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Invest in training analysts to code&lt;/strong&gt; (bite-sized, structured, mandatory).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assign companion developers&lt;/strong&gt; - dedicated engineers working side-by-side with analysts.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is similar to how &lt;strong&gt;civil engineers complement architects&lt;/strong&gt;. The developer's role: implement, optimize, and review code, leaving analysts free to model and interpret.&lt;/p&gt;
&lt;p&gt;A junior-to-mid-level OOP developer (familiar with C-family languages) costs \~\$100K and can support 2–3 analysts effectively. This model &lt;strong&gt;reduces headcount while increasing productivity.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-integrated-big-picture&quot;&gt;The Integrated Big Picture&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Eliminating redundant tech stacks is the single most powerful way to reduce technical debt.&lt;/strong&gt; In most organizations, essential functions are scattered across dozens of tools, each requiring specialized training.&lt;/p&gt;
&lt;p&gt;The solution is to &lt;strong&gt;build reusable internal libraries&lt;/strong&gt;, APIs, and tooling ecosystems around key functions (data ingestion, transformation, reporting, access control). And yes, this requires investment - but for a firm managing billions, it's well worth it.&lt;/p&gt;
&lt;p&gt;Open-source solutions often provide better APIs and lower long-term cost. But the goal isn't cost reduction per se - it's &lt;strong&gt;control, efficiency, and resilience&lt;/strong&gt;. Cost savings will follow.&lt;/p&gt;
&lt;h3 id=&quot;the-all-rounded-technical-analyst-model&quot;&gt;The All-Rounded Technical Analyst Model&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Divooka&lt;/em&gt; is our vision for what this looks like in practice: a unified computing platform combining:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enterprise-grade &lt;strong&gt;C#&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;SQL-dominant ETL pipelines&lt;/li&gt;
&lt;li&gt;Python's rich data science ecosystem&lt;/li&gt;
&lt;li&gt;A modular, plugin-based architecture&lt;/li&gt;
&lt;li&gt;Low-code/no-code &lt;strong&gt;visual programming&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Fully local, one-click deployment&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result: self-contained, auditable, maintainable pipelines that cover 90%+ of analytical needs, with fewer components and shallower learning curves.&lt;/p&gt;
&lt;p&gt;This abstraction allows analysts to become full-stack operators - no longer needing to juggle five tools for one task.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;The winning stack: C# + Divooka + Python + PostgreSQL + ADO + GitHub + microservices.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;related-topics&quot;&gt;Related Topics&lt;/h3&gt;
&lt;p&gt;These toolsets raise natural questions about &lt;strong&gt;hiring and training&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What kind of analysts should we hire?&lt;/li&gt;
&lt;li&gt;How fast can we get them productive?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From an engineering standpoint, we want people with &lt;strong&gt;structured programming experience&lt;/strong&gt; - even in entry-level roles. No one flies a plane without a license; &lt;strong&gt;why run production data systems without proper coding fundamentals?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Traditional programming bootcamps won't suffice. But a modern analyst can get up to speed in 3 months - if trained with the right curriculum: &lt;strong&gt;Excel + SQL + Divooka&lt;/strong&gt; (Yes, Excel is still needed for practical reasons). That's all you need for 80% of the analytical workload.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-9&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This article outlines an ideal - but attainable - vision for computing in complex industries.&lt;/p&gt;
&lt;p&gt;These problems can be solved with or without technology. But solving them well requires the &lt;strong&gt;right mindset, structure, and investment&lt;/strong&gt;. Every organization has unique needs, and solutions should be &lt;strong&gt;iterative, adaptive, and people-centered&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If any part of this resonates with your experience, I'd be happy to continue the conversation.&lt;/p&gt;
&lt;h3 id=&quot;changelog&quot;&gt;Changelog&lt;/h3&gt;
&lt;p&gt;20250509 Rev 001: Initial draft.&lt;br&gt;
20250520 Rev 002: Refine ideas.&lt;br&gt;
20250609 Rev 003: Adjust wording and refine ideas, staging for publication.&lt;br&gt;
20250620 Rev 004: Polishing.&lt;/p&gt;
&lt;h3 id=&quot;see-also&quot;&gt;See also&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/thought-20250620-the-plan-to-supersede-excel-4bje&quot;&gt;[Review] 20250620: The Plan to Supersede Excel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Data Science</category>
<guid isPermaLink="true">https://coderlegion.com/3378/computing-in-the-financial-sector-an-ideal-scenario</guid>
<pubDate>Fri, 20 Jun 2025 22:43:14 +0000</pubDate>
</item>
<item>
<title>[Review] 20250620: The Plan to Supersede Excel</title>
<link>https://coderlegion.com/3376/review-20250620-the-plan-to-supersede-excel</link>
<description>&lt;p&gt;Soon enough, Divooka is almost here. Let's go back to the problem that got us started: practical data analytics at scale.&lt;/p&gt;
&lt;p&gt;We're not trying to completely replace Excel. Excel serves its purposes extremely well, and we envision a world where tools are more specialized depending on the needs. On the other hand, many of the problems Divooka aims to solve - particularly in data analytics - stem from &lt;em&gt;overuse&lt;/em&gt; or &lt;em&gt;misuse&lt;/em&gt; of Excel in situations it was never designed for. That's why Excel naturally becomes a benchmark for evaluating how well Divooka addresses these challenges - and in turn, how effectively Divooka can substitute for Excel in those contexts.&lt;/p&gt;
&lt;p&gt;In general, the future we're building toward is one where people only use Excel occasionally - for working with legacy systems or exchanging files with external clients. For all other practical, internal, and repeatable analytical tasks, organizations and individuals shouldn't have to open Excel at all. That's the vision for Divooka in the data analytics space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt; If you're wondering why we compare ourselves to Excel when talking about &quot;data analytics&quot; rather than Python - it's because we're focusing on where actual work gets done by everyday professionals in finance, retail, consulting, etc. - outside of data scientists and machine learning engineers, who typically work within well-defined infrastructures or theoretical environments.&lt;/p&gt;
&lt;h3 id=&quot;overview-13&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;Excel offers the following main functionalities and use cases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Using worksheets/workbooks as informal databases – enabling data entry, filtering, etc.&lt;/li&gt;
&lt;li&gt;Applying formulas for analytics – calculating sums, performing aggregates, lookups.&lt;/li&gt;
&lt;li&gt;Plotting and reporting – generating charts and formatted reports.&lt;/li&gt;
&lt;li&gt;Automating results and, to some extent, content – via templates for repeated use.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;a-note-on-learning&quot;&gt;A Note on Learning&lt;/h3&gt;
&lt;p&gt;Some claim Excel is easier to learn than Python or Divooka. In our experience, that perception usually comes from people who've been using Excel for 10+ years. That certainly wasn't my experience when I first tried to learn it - by then, I was already an experienced programmer.&lt;/p&gt;
&lt;p&gt;Excel has a lot of quirks. Fortunately, it also has a wealth of online resources - forums, tutorials, and StackOverflow answers - that help users troubleshoot specific needs quickly. But that doesn't necessarily make it easier to learn.&lt;/p&gt;
&lt;p&gt;Meanwhile, its automation and extension system (VBA, VSTO) and DOM model are far from elegant. They can be powerful - but they're also idiosyncratic, outdated, and hard to scale or maintain.&lt;/p&gt;
&lt;h3 id=&quot;identifying-veteran-use&quot;&gt;Identifying Veteran Use&lt;/h3&gt;
&lt;p&gt;For veteran users - those who've worked in finance, consulting, banking, or retail for over a decade - it's crucial to identify what matters most to them so we don't waste time trying to rebuild everything from scratch.&lt;/p&gt;
&lt;p&gt;For these professionals, usually in management roles, key priorities tend to be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Simple data entry&lt;/li&gt;
&lt;li&gt;Basic formulas (especially VLOOKUP and IF logic) and pivot tables&lt;/li&gt;
&lt;li&gt;Minimal plotting&lt;/li&gt;
&lt;li&gt;Extensive styling, including conditional formatting&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Veterans rarely write VBA or build complex automated spreadsheets themselves - that's what interns and junior staff are usually tasked with.&lt;/p&gt;
&lt;h3 id=&quot;the-solution-plan&quot;&gt;The Solution Plan&lt;/h3&gt;
&lt;p&gt;Divooka already outperforms Excel in areas like analytics and automation (points 3 and 4), but matching Excel's GUI experience for basic data entry and formulas remains a challenge.&lt;/p&gt;
&lt;h4 id=&quot;the-challenges&quot;&gt;The Challenges&lt;/h4&gt;
&lt;p&gt;The problem can be broken down into two fronts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We must provide a familiar interface and usage pattern - even though Divooka is not Excel - to reduce resistance from long-time Excel users.&lt;/li&gt;
&lt;li&gt;Divooka is a visual programming language with its own design principles. While we want to be &lt;em&gt;similar to Excel&lt;/em&gt; where needed, we must stay true to what makes Divooka powerful and unique.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;the-approaches&quot;&gt;The Approaches&lt;/h4&gt;
&lt;p&gt;To address the GUI/database usage pattern, we're exploring a few possible approaches:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a custom visual node that replicates the structure of a standard Excel worksheet - though it might look more like a clean, modern take similar to Apple Numbers.&lt;/li&gt;
&lt;li&gt;Encourage users to adopt API-driven or externalized databases and foster a mindset of separating &lt;em&gt;data&lt;/em&gt;, &lt;em&gt;computation&lt;/em&gt;, and &lt;em&gt;presentation&lt;/em&gt;. While we offer SQL and database API integration out of the box, we also recognize that many users resist this - even if they're already managing convoluted Excel formulas.&lt;/li&gt;
&lt;li&gt;On that note, there are two directions we're exploring:&lt;ul&gt;
&lt;li&gt;Provide a visual interface (like MySQL Workbench) for common database operations.&lt;/li&gt;
&lt;li&gt;Offer a high-level, node-based API that avoids manual SQL writing, while gently encouraging users to become more familiar with what's happening under the hood.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;a-combined-use-case&quot;&gt;A Combined Use Case&lt;/h4&gt;
&lt;p&gt;The most common (and most problematic) Excel use case is when people combine:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Data entry,&lt;/li&gt;
&lt;li&gt;Basic automated calculations,&lt;/li&gt;
&lt;li&gt;Fancy styling,&lt;/li&gt;
&lt;li&gt;Plots and form updates for reporting purposes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is where Excel shines - and also where it often gets &lt;em&gt;abused&lt;/em&gt;. It enables incredibly compact and self-contained workflows: enter numbers, add formulas, apply some styling - and voilà, you have a polished report.&lt;/p&gt;
&lt;p&gt;But this convenience comes at a cost: poor scalability, maintainability issues, and a lack of clear separation of concerns. A single cell can simultaneously serve as a data source, a computed result, and a styled report element. This makes downstream changes, automation, and collaboration extremely hard.&lt;/p&gt;
&lt;p&gt;The traditional software engineering solution is seen in the web: HTML (structure), CSS (style), and JavaScript (logic). By clearly separating concerns, we maintain flexibility and clarity. So what if creating an HTML/CSS/JS-based dashboard was &lt;em&gt;as easy&lt;/em&gt; as building an Excel sheet? That's one possible angle - and something Divooka could eventually support through node APIs or higher-level content editors.&lt;/p&gt;
&lt;p&gt;But that's not the most elegant path forward.&lt;/p&gt;
&lt;h4 id=&quot;the-real-solution&quot;&gt;The Real Solution&lt;/h4&gt;
&lt;p&gt;The better question to ask is: what is the actual &lt;em&gt;end goal&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;People want to build interactive dashboards or reports that auto-update, look professional, and live within a single workspace where they can easily edit data, adjust formulas, and customize visuals - all while staying clean and maintainable.&lt;/p&gt;
&lt;p&gt;Wait a second - doesn't that sound like &lt;strong&gt;&lt;em&gt;App Mode&lt;/em&gt;&lt;/strong&gt; in Neo?&lt;/p&gt;
&lt;p&gt;(Screenshot coming soon when the time is right.)&lt;/p&gt;
&lt;h3 id=&quot;conclusion-10&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Divooka was never about &quot;replacing Excel&quot; outright. That was never the goal - and still isn't. What we set out to solve was broader and deeper: making practical, scalable data analytics accessible to professionals who are otherwise stuck with brittle, overextended workflows. Excel simply happens to be the dominant tool in that space, so it naturally became both our benchmark and our foil.&lt;/p&gt;
&lt;p&gt;In a sense, replacing Python is an easier problem - just design a better language, with better libraries and ecosystem. Julia is doing that, so are many others. When we started tackling &quot;The Excel Problem,&quot; it felt vague and enormous. We only had instincts: &quot;There must be a better way,&quot; and &quot;Visual programming, like Divooka, seems right.&quot; Back then, we didn't yet see the full shape of the solution.&lt;/p&gt;
&lt;p&gt;But as Divooka matures, as our libraries evolve, and as our design principles solidify, the picture becomes clearer.&lt;/p&gt;
&lt;p&gt;It's becoming evident that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Data entry can be streamlined through a combination of high-level APIs and dedicated GUIs.&lt;/li&gt;
&lt;li&gt;Dedicated graphical editors are not just useful - they're &lt;em&gt;the future&lt;/em&gt;. We'll share more on this next year.&lt;/li&gt;
&lt;li&gt;Divooka's graph-based mindset is not only powerful but &lt;em&gt;practical&lt;/em&gt;. Neo will serve as the reference implementation and the litmus test for what a modern visual programming platform can achieve.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Of course, there are still many UX and ergonomics problems to solve - and we must proceed with care.&lt;/p&gt;
&lt;p&gt;But we're getting close.&lt;/p&gt;
&lt;h3 id=&quot;resources&quot;&gt;Resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/Divooka/Techniques/AppMode&quot;&gt;App Mode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Divooka</category>
<guid isPermaLink="true">https://coderlegion.com/3376/review-20250620-the-plan-to-supersede-excel</guid>
<pubDate>Fri, 20 Jun 2025 13:50:46 +0000</pubDate>
</item>
<item>
<title>Topic 20250618: Procedural Context is More Learning (and Syntax) to Users</title>
<link>https://coderlegion.com/3363/topic-20250618-procedural-context-is-more-learning-and-syntax-to-users</link>
<description>&lt;h3 id=&quot;overview-14&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;When people first open Divooka, they see a network of interconnected nodes where connections represent data flow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/09w4h4ekh092007lcieh.png&quot; alt=&quot;Hello World in Divooka&quot;&gt;&lt;/p&gt;
&lt;p&gt;Even someone who has never seen visual programming or Divooka before can read the titles of the nodes and input parameters and have a pretty good guess at what the program does.&lt;/p&gt;
&lt;p&gt;Thus, a key to using Divooka (in the dataflow context) efficiently is understanding the execution runtime - what nodes are active, what the dependency tree is, and how caching works.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qff3yneo64t71mme33a0.png&quot; alt=&quot;Canonical Example of Divooka Program&quot;&gt;&lt;/p&gt;
&lt;p&gt;As someone who has been using Unreal Engine Blueprint since 2014, the concept of &quot;execution flow&quot; appears natural to me - but when showing this to new users, it became evident that it takes time to tune in and learn to read the diagrams.&lt;/p&gt;
&lt;p&gt;We'll take a look at what procedural context is, then briefly analyze why that's the case.&lt;/p&gt;
&lt;h3 id=&quot;the-procedural-constructs&quot;&gt;The Procedural Constructs&lt;/h3&gt;
&lt;p&gt;The procedural context in Divooka provides many constructs that may be familiar to those from traditional programming languages.&lt;/p&gt;
&lt;p&gt;An example is shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xcspn3im5zkka4w0qcco.png&quot; alt=&quot;Procedural Example&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;common-constructs&quot;&gt;Common Constructs&lt;/h4&gt;
&lt;p&gt;Conditional logic is done using the &lt;strong&gt;Branch&lt;/strong&gt; node.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pbihzzew385ja3qhtiwd.png&quot; alt=&quot;Branch Node&quot;&gt;&lt;/p&gt;
&lt;p&gt;Loops are implemented using the &lt;strong&gt;Repeat&lt;/strong&gt; node.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/thv3amrjl4i1ufrupazg.png&quot; alt=&quot;Repeat Node&quot;&gt;&lt;/p&gt;
&lt;p&gt;Iteration is done using the &lt;strong&gt;For Each&lt;/strong&gt; node.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ql7n4ejel01nehp9dqum.png&quot; alt=&quot;For Each Node&quot;&gt;&lt;/p&gt;
&lt;p&gt;Functions are handled using the &lt;strong&gt;Execute Event&lt;/strong&gt; and &lt;strong&gt;Custom Event&lt;/strong&gt; nodes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sc114ttylctvk2ud65mt.png&quot; alt=&quot;Custom Events&quot;&gt;&lt;/p&gt;
&lt;p&gt;A switch-case structure is implemented using the &lt;strong&gt;Switch&lt;/strong&gt; node.&lt;/p&gt;
&lt;p&gt;(Pending image)&lt;/p&gt;
&lt;h4 id=&quot;unique-constructs&quot;&gt;Unique Constructs&lt;/h4&gt;
&lt;p&gt;In addition, Divooka also offers constructs that are unique to it:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sequence&lt;/strong&gt; nodes are used to define multiple sequential steps.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zk5upgtqff6xbbatxrvu.png&quot; alt=&quot;Sequence Node&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Events&lt;/strong&gt; are entry points defined by the framework (procedural context base type).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z5xv5n64dbesogk75inv.png&quot; alt=&quot;Event Example&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;implementation-note&quot;&gt;Implementation Note&lt;/h4&gt;
&lt;p&gt;Certain constructs are currently missing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Break&lt;/strong&gt;: to exit a loop prematurely.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;caveats-of-states&quot;&gt;Caveats of States&lt;/h4&gt;
&lt;p&gt;What's more, in procedural context there is the possibility of manipulating state - for example, incrementing a number to be used next time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xhoq417eu65xdj97z511.png&quot; alt=&quot;State Variable Example&quot;&gt;&lt;/p&gt;
&lt;p&gt;Thus, to use the procedural context effectively, users need to learn a good amount of &quot;syntax&quot;: control flow, state handling, and more.&lt;/p&gt;
&lt;h3 id=&quot;the-two-contexts&quot;&gt;The Two Contexts&lt;/h3&gt;
&lt;p&gt;Given that all of the top 10 most popular programming languages support imperative/procedural programming as the default mode, one might expect this to be the most straightforward paradigm. In reality, that's not the case.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uhjzx1by2t1sf2ertcj8.png&quot; alt=&quot;The Two Contexts&quot;&gt;&lt;/p&gt;
&lt;p&gt;Use the &lt;strong&gt;dataflow context&lt;/strong&gt; for data-centric operations, such as data transformations.&lt;/p&gt;
&lt;p&gt;Use the &lt;strong&gt;procedural context&lt;/strong&gt; for logic-centric operations, such as branching and conditional executions.&lt;/p&gt;
&lt;p&gt;Unlike procedural context, when using dataflow context, users need to learn only a single syntax: connections represent data flows, and everything (almost) always executes - depending on node activation status. To follow the flow of a program is simply to follow the connections - there's no concern about branches or alternate execution paths.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-11&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;The procedural context is more complex for several reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Connections now represent both data and execution flow&lt;/li&gt;
&lt;li&gt;Some nodes can interrupt, diverge from, or selectively execute downstream connections - requiring users to understand exact behaviors to make accurate predictions&lt;/li&gt;
&lt;li&gt;There are many types of flow control nodes, each requiring learning&lt;/li&gt;
&lt;li&gt;State is involved&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Part of the reason Divooka started with the dataflow context was exactly to reduce this complexity. However, as programs become more complicated and procedural logic becomes involved, it's not possible to avoid procedural context - in fact, having it is often more convenient than not.&lt;/p&gt;
&lt;p&gt;Users must make informed decisions about what to put into procedural context and what to offload to dataflow context.&lt;/p&gt;
&lt;h3 id=&quot;references-12&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/Divooka/Language/DataflowContext&quot;&gt;Dataflow context in Divooka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/Divooka/Language/ProceduralContext&quot;&gt;Procedural context in Divooka&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>C#</category>
<guid isPermaLink="true">https://coderlegion.com/3363/topic-20250618-procedural-context-is-more-learning-and-syntax-to-users</guid>
<pubDate>Wed, 18 Jun 2025 17:44:27 +0000</pubDate>
</item>
<item>
<title>DevLog 20250613: Ol'ista Web Framework</title>
<link>https://coderlegion.com/3333/devlog-20250613-olista-web-framework</link>
<description>&lt;h2 id=&quot;overview-15&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;For web app development, we need a framework for Divooka. To approach this problem, we study existing frameworks in existing languages.&lt;/p&gt;
&lt;h3 id=&quot;api-style-survey&quot;&gt;API Style Survey&lt;/h3&gt;
&lt;p&gt;If we look at any existing web server API, like &lt;code&gt;ASP.Net Core&lt;/code&gt; below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;using Microsoft.Extensions.FileProviders;

var builder = WebApplication.CreateBuilder(args);
var app     = builder.Build();

// Serve default files like index.html
app.UseDefaultFiles();
// Serve static files from wwwroot/
app.UseStaticFiles();

app.Run();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or &lt;code&gt;Flask&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from flask import Flask

app = Flask(
    __name__,
    static_folder='static',      # default
    static_url_path=''           # serve at root instead of '/static'
)

@app.route('/')
def index():
    # Sends static/index.html
    return app.send_static_file('index.html')

if __name__ == '__main__':
    app.run(debug=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or NodeJS:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// server.js
import { createServer } from 'http';
import { createReadStream, statSync } from 'fs';
import { extname, join } from 'path';

const PUBLIC_DIR = join(process.cwd(), 'public');

createServer((req, res) =&amp;gt; {
  // Map URL → file path
  const urlPath = req.url === '/' ? '/index.html' : req.url;
  const filePath = join(PUBLIC_DIR, urlPath);

  try {
    const stats = statSync(filePath);
    if (stats.isFile()) {
      const stream = createReadStream(filePath);
      // Minimal mime-type handling:
      const ext = extname(filePath).slice(1);
      res.setHeader('Content-Type', {
        html: 'text/html',
        js:   'application/javascript',
        css:  'text/css',
        png:  'image/png',
        jpg:  'image/jpeg',
      }[ext] || 'application/octet-stream');
      stream.pipe(res);
      return;
    }
  } catch {
    // fall through to 404
  }

  res.statusCode = 404;
  res.end('Not found');
}).listen(3000, () =&amp;gt; console.log('Listening on http://localhost:3000'));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;code&gt;Express.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// app.js
import express from 'express';
const app = express();

// Serve index.html automatically at '/'
app.use(express.static('public'));

// (Optional) Serve another folder at /assets
app.use('/assets', express.static('PublicAssets'));

app.listen(3000, () =&amp;gt; {
  console.log('Static server running on http://localhost:3000');
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are some common features:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We define what the server does&lt;/li&gt;
&lt;li&gt;We provide some &lt;em&gt;&quot;endpoints&quot;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;And we let the server run wild&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There is an obvious data-driven pattern. Now, if you have followed Divooka's development, or have seen a few use cases of the &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Divooka/Language/Contexts&quot;&gt;Dataflow context&lt;/a&gt; (e.g. with &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodox/devlog-20250610-plotting-in-divooka-7f8&quot;&gt;plotting&lt;/a&gt;), it would immediately appear to you: &lt;strong&gt;&lt;em&gt;wow, we should be able to do this trivially in Divooka!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Indeed it is!&lt;/p&gt;
&lt;h3 id=&quot;ol-ista---the-divooka-way&quot;&gt;Ol'ista - The Divooka Way&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Frameworks/Oista&quot;&gt;Ol'ista&lt;/a&gt; (codename &lt;code&gt;Oista&lt;/code&gt;) is Divooka Explore's builtin web framework.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;web server&lt;/strong&gt; is configured using &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Frameworks/Oista/Capabilities&quot;&gt;capabilities&lt;/a&gt;, those include: static file hosting, FTP server, web API, etc.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Serve Static Files&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://publication.tor1.cdn.digitaloceanspaces.com/Websites/MethodoxWiki/Images/Framework-Oista-Static_File_Server.png&quot; alt=&quot;Serve Static Files&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create FTP Server&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://publication.tor1.cdn.digitaloceanspaces.com/Websites/MethodoxWiki/Images/Framework-Oista-Capabilities-FTP_Server.png&quot; alt=&quot;Create FTP Server&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Serve Web API&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/69ohapt7jyac6gzhovhp.png&quot; alt=&quot;Serve Web API&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;standard-components&quot;&gt;Standard Components&lt;/h4&gt;
&lt;p&gt;For most common things that almost all web frameworks provide, Ol'ista provides it through the &lt;strong&gt;Static File Capability&lt;/strong&gt; and &lt;strong&gt;Web API Capability&lt;/strong&gt;. Configuration is done through succession of compositional APIs:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u29ynh4wn0jyrw4hqigc.png&quot; alt=&quot;Compositional Configuration&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can serve many capabilities at once:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pvk8revp6kzfysy4iqd5.png&quot; alt=&quot;Serve multiple capabilities&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;dynamic-behaviors&quot;&gt;Dynamic Behaviors&lt;/h4&gt;
&lt;p&gt;Dynamic behaviors or what's typically understood as web APIs are served through &lt;code&gt;Web API Capability&lt;/code&gt;, and Divooka enables it through &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Divooka/Language/FunctionReference&quot;&gt;function references&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Server Side&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lgiwgsnaeg82cu9e10ki.png&quot; alt=&quot;Server Side&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Full Setup (Server and Client)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d1gkm4c582twcgi0iorq.png&quot; alt=&quot;Server &amp;amp; Client Setup&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Result on Client&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2r3z7xjh8y4sey3g3rf0.png&quot; alt=&quot;Request Result&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;dynamic-webpages&quot;&gt;Dynamic Webpages&lt;/h4&gt;
&lt;p&gt;Most web frameworks offers some kind of template language for easier generated HTML contents.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ASP .NET Core (Razor)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Views/Shared/_Layout.cshtml&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;utf-8&quot; /&amp;gt;
    &amp;lt;title&amp;gt;@ViewData[&quot;Title&quot;] – MyApp&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;header&amp;gt;
        @await Html.PartialAsync(&quot;_Nav&quot;)
    &amp;lt;/header&amp;gt;

    &amp;lt;main role=&quot;main&quot;&amp;gt;
        @RenderBody()
    &amp;lt;/main&amp;gt;

    &amp;lt;footer&amp;gt;
        @RenderSection(&quot;Scripts&quot;, required: false)
    &amp;lt;/footer&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Views/Home/Index.cshtml&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;@{
    ViewData[&quot;Title&quot;] = &quot;Home&quot;;
    var items = new[] { &quot;Apple&quot;, &quot;Banana&quot;, &quot;Cherry&quot; };
}
&amp;lt;h1&amp;gt;Hello, @User.Identity.Name!&amp;lt;/h1&amp;gt;

&amp;lt;ul&amp;gt;
@foreach (var item in items)
{
    &amp;lt;li&amp;gt;@item&amp;lt;/li&amp;gt;
}
&amp;lt;/ul&amp;gt;

@section Scripts {
    &amp;lt;script&amp;gt;console.log(&quot;Page loaded&quot;);&amp;lt;/script&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Python Flask (Jinja2)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;templates/base.html&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;{% block title %}MyApp{% endblock %}&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;header&amp;gt;
      {% include &quot;nav.html&quot; %}
    &amp;lt;/header&amp;gt;

    &amp;lt;main&amp;gt;
      {% block content %}{% endblock %}
    &amp;lt;/main&amp;gt;

    &amp;lt;footer&amp;gt;
      {% block scripts %}{% endblock %}
    &amp;lt;/footer&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;templates/index.html&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;{% extends &quot;base.html&quot; %}

{% block title %}Home – MyApp{% endblock %}

{% block content %}
  &amp;lt;h1&amp;gt;Hello, {{ current_user.name }}!&amp;lt;/h1&amp;gt;
  &amp;lt;ul&amp;gt;
    {% for fruit in [&quot;Apple&quot;,&quot;Banana&quot;,&quot;Cherry&quot;] %}
      &amp;lt;li&amp;gt;{{ fruit }}&amp;lt;/li&amp;gt;
    {% endfor %}
  &amp;lt;/ul&amp;gt;
{% endblock %}

{% block scripts %}
  &amp;lt;script&amp;gt;console.log(&quot;Page loaded&quot;);&amp;lt;/script&amp;gt;
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Ruby on Rails (ERB)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;app/views/layouts/application.html.erb&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;
  &amp;lt;title&amp;gt;&amp;lt;%= content_for?(:title) ? yield(:title) : &quot;MyApp&quot; %&amp;gt;&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;header&amp;gt;
    &amp;lt;%= render &quot;nav&quot; %&amp;gt;
  &amp;lt;/header&amp;gt;

  &amp;lt;main&amp;gt;
    &amp;lt;%= yield %&amp;gt;
  &amp;lt;/main&amp;gt;

  &amp;lt;footer&amp;gt;
    &amp;lt;%= yield :scripts if content_for?(:scripts) %&amp;gt;
  &amp;lt;/footer&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;app/views/home/index.html.erb&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;% content_for :title, &quot;Home – MyApp&quot; %&amp;gt;

&amp;lt;h1&amp;gt;Hello, &amp;lt;%= current_user.name %&amp;gt;!&amp;lt;/h1&amp;gt;

&amp;lt;ul&amp;gt;
  &amp;lt;% [&quot;Apple&quot;,&quot;Banana&quot;,&quot;Cherry&quot;].each do |fruit| %&amp;gt;
    &amp;lt;li&amp;gt;&amp;lt;%= fruit %&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;% end %&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;% content_for :scripts do %&amp;gt;
  &amp;lt;script&amp;gt;console.log(&quot;Page loaded&quot;);&amp;lt;/script&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Among those, ASP.Net Core and Ruby on Rails appear cleaner.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Comparison&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Feature                   &lt;/th&gt;&lt;th&gt;ASP .NET Core (Razor)              &lt;/th&gt;&lt;th&gt;Python Flask (Jinja2)                          &lt;/th&gt;&lt;th&gt;Ruby on Rails (ERB)&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;File extension            &lt;/td&gt;&lt;td&gt;&lt;code&gt;.cshtml&lt;/code&gt;                          &lt;/td&gt;&lt;td&gt;&lt;code&gt;.html&lt;/code&gt; (or &lt;code&gt;.jinja2&lt;/code&gt;)                         &lt;/td&gt;&lt;td&gt;&lt;code&gt;.html.erb&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Expression delimiter      &lt;/td&gt;&lt;td&gt;&lt;code&gt;@Model.Property&lt;/code&gt; or &lt;code&gt;@(...)&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;&lt;code&gt;{{ variable }}&lt;/code&gt;                               &lt;/td&gt;&lt;td&gt;&lt;code&gt;&amp;lt;%= @variable %&amp;gt;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Statement delimiter       &lt;/td&gt;&lt;td&gt;&lt;code&gt;@{ ... }&lt;/code&gt;, &lt;code&gt;@if(...) { ... }&lt;/code&gt;     &lt;/td&gt;&lt;td&gt;&lt;code&gt;{% ... %}&lt;/code&gt;                                    &lt;/td&gt;&lt;td&gt;&lt;code&gt;&amp;lt;% ... %&amp;gt;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Partial/templates include &lt;/td&gt;&lt;td&gt;&lt;code&gt;@await Html.PartialAsync(&quot;_Nav&quot;)&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;{% include &quot;nav.html&quot; %}&lt;/code&gt;                     &lt;/td&gt;&lt;td&gt;&lt;code&gt;&amp;lt;%= render &quot;nav&quot; %&amp;gt;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For Divooka, as a start, I plan to implement something I call &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Frameworks/Oista/Markit&quot;&gt;Markit&lt;/a&gt; - a simple string replacement. This awaits further work and will be updated on Ol'ista's &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Frameworks/Oista&quot;&gt;wiki&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;references-13&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Frameworks/Oista&quot;&gt;Ol'ista framework&lt;/a&gt; for Divooka&lt;/li&gt;
&lt;li&gt;Full setup walkthrough for the &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Tutorials/WebServices/WebAPIServer&quot;&gt;example&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>RESTful APIs</category>
<guid isPermaLink="true">https://coderlegion.com/3333/devlog-20250613-olista-web-framework</guid>
<pubDate>Fri, 13 Jun 2025 20:42:59 +0000</pubDate>
</item>
<item>
<title>DevLog 20250611: Audio API Design for Divooka Glaze!</title>
<link>https://coderlegion.com/3324/devlog-20250611-audio-api-design-for-divooka-glaze</link>
<description>&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Glaze!&lt;/strong&gt; is an interactive media framework in Divooka that features a &lt;a rel=&quot;nofollow&quot; href=&quot;https://processing.org/&quot;&gt;Processing&lt;/a&gt;-like interface.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;overview-16&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;Today, I'm laying the groundwork for audio APIs for our &lt;strong&gt;Glaze!&lt;/strong&gt; framework.&lt;/p&gt;
&lt;p&gt;Divooka &lt;strong&gt;Glaze!&lt;/strong&gt; draws inspiration from SFML and Processing, so we need to understand how those two handle audio first.&lt;/p&gt;
&lt;h3 id=&quot;how-sfml-handles-it-sfml-net-&quot;&gt;How SFML Handles It (SFML.Net)&lt;/h3&gt;
&lt;p&gt;To play audio in SFML.Net, we use the &lt;strong&gt;SFML.Audio&lt;/strong&gt; module, which provides two main classes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Sound&lt;/code&gt;&lt;/strong&gt; + &lt;strong&gt;&lt;code&gt;SoundBuffer&lt;/code&gt;&lt;/strong&gt; for short, fully-loaded sounds (like sound effects).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Music&lt;/code&gt;&lt;/strong&gt; for long, streamed audio (like background music).&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;playing-short-sounds&quot;&gt;Playing Short Sounds&lt;/h4&gt;
&lt;p&gt;Short audio files (WAV, OGG, FLAC, etc.) can be fully loaded into memory:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;// Load the entire file into a buffer (only do this once per sound).
SoundBuffer buffer = new SoundBuffer(&quot;hit.wav&quot;);

// Create a Sound instance and assign the buffer.
Sound hitSound = new Sound(buffer);

// Optional settings
hitSound.Volume = 75f;    // 0–100
hitSound.Pitch  = 1.2f;   // playback speed
hitSound.Loop   = false;  // play once

// Play it
hitSound.Play();

// Make sure the program doesn't exit immediately,
// otherwise the sound will be cut off:
while (hitSound.Status == SoundStatus.Playing)
    System.Threading.Thread.Sleep(10);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reuse&lt;/strong&gt; the same &lt;code&gt;SoundBuffer&lt;/code&gt; if you need to play the same sound multiple times.&lt;/li&gt;
&lt;li&gt;Keep the &lt;code&gt;Sound&lt;/code&gt; (and its &lt;code&gt;SoundBuffer&lt;/code&gt;) in scope until playback finishes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;streaming-music&quot;&gt;Streaming Music&lt;/h4&gt;
&lt;p&gt;For large files (e.g., full-length tracks), use the &lt;code&gt;Music&lt;/code&gt; class, which streams from disk:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It doesn't load the entire file into RAM.&lt;/li&gt;
&lt;li&gt;Suitable for long tracks, though with slightly higher CPU usage due to streaming.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;// Create a Music instance; it will stream the file on the fly.
Music backgroundMusic = new Music(&quot;background.ogg&quot;);

// Optional settings
backgroundMusic.Volume = 50f;
backgroundMusic.Loop   = true;

// Start playback
backgroundMusic.Play();

// In a game loop we'd typically:
// while (window.IsOpen()) { /* update, draw, etc. */ }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;common-pitfalls&quot;&gt;Common Pitfalls&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Missing Native DLLs&lt;/strong&gt;: If you get &lt;code&gt;DllNotFoundException&lt;/code&gt;, ensure all CSFML native libraries are present.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scope &amp;amp; Lifetime&lt;/strong&gt;: If &lt;code&gt;Sound&lt;/code&gt; or &lt;code&gt;Music&lt;/code&gt; goes out of scope (is garbage collected), playback stops.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Program Exit&lt;/strong&gt;: If the application ends before the audio finishes, you won't hear the full sound - keep the main thread running or integrate playback into the render loop.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;how-processing-handles-it&quot;&gt;How Processing Handles It&lt;/h3&gt;
&lt;p&gt;Processing offers sound support through the built-in Sound library and the legacy Minim library.&lt;/p&gt;
&lt;p&gt;Below are the two most common approaches.&lt;/p&gt;
&lt;h4 id=&quot;the-built-in-sound-library&quot;&gt;The Built-in Sound Library&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Installing/Importing&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In the Processing IDE, go to &lt;strong&gt;Sketch → Import Library… → Add Library…&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;“Sound”&lt;/strong&gt;, then click &lt;strong&gt;Install&lt;/strong&gt; (if not already installed).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Placing Audio Files&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a folder named &lt;code&gt;data&lt;/code&gt; in the sketch's directory.&lt;/li&gt;
&lt;li&gt;Copy audio files (e.g., &lt;code&gt;music.mp3&lt;/code&gt; or &lt;code&gt;beat.wav&lt;/code&gt;) into that &lt;code&gt;data&lt;/code&gt; folder.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the sketch, import and declare:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import processing.sound.*;
SoundFile song;

void setup() {
  size(400, 200);
  // Load a file named &quot;music.mp3&quot; from /data
  song = new SoundFile(this, &quot;music.mp3&quot;);
  song.play();        // start playing once
}

void draw() {
  background(0);
  // (Optionally visualize or control playback here)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Looping, Volume, Rate&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;void setup() {
  size(400, 200);
  song = new SoundFile(this, &quot;music.mp3&quot;);
  
  song.loop();               // loop indefinitely
  song.amp(0.5);             // set volume (0.0–1.0)
  song.rate(1.2);            // change playback speed (1.0 = normal)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;minim&quot;&gt;Minim&lt;/h4&gt;
&lt;p&gt;Minim handles things similarly, albeit through &lt;code&gt;AudioPlayer&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import ddf.minim.*;
Minim minim;
AudioPlayer player;

void setup() {
  size(400, 200);
  
  minim = new Minim(this);
  // loadFile(filename, bufferSize)
  player = minim.loadFile(&quot;music.mp3&quot;, 2048); // Place MP3/WAV files inside the sketch's `data` folder.
  player.play();
  
  // Looping &amp;amp; Controlling
  player.loop();        // loop playback
  player.setGain(-10);  // lower volume: gain in dB
  player.setTempo(1.5); // speed multiplier
}

void draw() {
  background(50);
  // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Where to Store Files&lt;/strong&gt;: Always place audio files in the sketch's &lt;code&gt;data/&lt;/code&gt; folder so Processing can find them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;File Formats&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;Sound library: supports WAV, AIFF, MP3 (depending on platform).&lt;/li&gt;
&lt;li&gt;Minim: supports MP3, OGG, WAV.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Latency / Real-Time&lt;/strong&gt;: For low-latency playback or live audio input/processing, Minim (or advanced libraries like &lt;strong&gt;Beads&lt;/strong&gt; or &lt;strong&gt;TarsosDSP&lt;/strong&gt;) may be preferable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visualizations&lt;/strong&gt;: Both libraries support real-time audio analysis (waveforms, FFT), which is great for interactive visuals.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;observations&quot;&gt;Observations&lt;/h3&gt;
&lt;p&gt;Those two statements are crucial:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// Load a soundfile from the /data folder of the sketch and play it back
file = new SoundFile(this, &quot;sample.mp3&quot;);
file.play();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can observe a few things regarding API design:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It's procedural and imperative - we create resources first, then set properties, then play/stop via the resource reference. Importantly, it's necessary to keep a reference for later control.&lt;/li&gt;
&lt;li&gt;Resources might require explicit (or scope-based) release.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On the other hand, I'm more interested in a &quot;functional&quot; style API, like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p6mgkkm5rdmftf7f1g7r.png&quot; alt=&quot;Audio API Concept&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-approach&quot;&gt;The Approach&lt;/h3&gt;
&lt;p&gt;We could implicitly load and manage resources, releasing them at the end of the application lifetime (on window close), if no explicit reference is needed. However, distinguishing between &lt;code&gt;SoundBuffer&lt;/code&gt; and &lt;code&gt;Sound&lt;/code&gt; (both are &lt;code&gt;IDisposable&lt;/code&gt;) can be tricky when deciding which to persist. At the very least, this is what we should expose via &lt;code&gt;PlaySound()&lt;/code&gt; and &lt;code&gt;PlayMusic()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ifkkz6igrkzxiyhb31l.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;private void ReleaseResources()
{
    // Sounds
    foreach (Sound item in _soundInstances)
        item.Dispose();
    foreach ((string key, SoundBuffer item) in _soundBuffers)
        item.Dispose();
    foreach (Music item in _music)
        item.Dispose();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In fact in the current design we cannot share the underlying instance - since &lt;strong&gt;Glaze!&lt;/strong&gt; is implementation-agnostic - unless we introduce strongly typed types for &lt;code&gt;SoundBuffer&lt;/code&gt;, &lt;code&gt;Sound&lt;/code&gt;, and &lt;code&gt;Music&lt;/code&gt;, so all implementations can recognize them. Otherwise, we can't return any instance objects.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;// IInteractiveServiceProvider, which is ignorant of SFML.Net's existence
#region Audio Engine
void PlaySound(string filePath);
void PlayMusic(string filePath, bool loop = true);
#endregion
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A slightly inelegant shortcut is to expose them as untyped &quot;object handles&quot;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;/// &amp;lt;summary&amp;gt;
/// One-shot sound.
/// &amp;lt;/summary&amp;gt;
object PlaySound(string filePath);
/// &amp;lt;summary&amp;gt;
/// Loopable BGM etc.
/// &amp;lt;/summary&amp;gt;
object PlayMusic(string filePath, bool loop = true);
/// &amp;lt;param name=&quot;volume&quot;&amp;gt;0–100f&amp;lt;/param&amp;gt;
void SetVolume(object soundOrMusic, float volume);
/// &amp;lt;param name=&quot;pitch&quot;&amp;gt;0–1f&amp;lt;/param&amp;gt;
void SetPitch(object soundOrMusic, float pitch);
void SetLoop(object music, bool loop);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While it might seem &quot;unsafe&quot; on the paper, it works well in Divooka's graphical interface:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wfrx2osj8j4zhwd8l1ue.png&quot; alt=&quot;Object Handle Looks Just Like Any Other Object on GUI - As If Strongly Typed&quot;&gt;&lt;/p&gt;
&lt;p&gt;(This is partly thanks to Divooka's support for return value naming via XML doc comments, e.g., &lt;code&gt;&amp;lt;returns name=&quot;Music&quot;&amp;gt;A music instance.&amp;lt;/returns&amp;gt;&lt;/code&gt; for &lt;code&gt;PlayMusic()&lt;/code&gt;.)&lt;/p&gt;
&lt;h3 id=&quot;conclusion-12&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;So far so good! There's a lot we can enhance in the audio engine - more parameters, DSP support, etc. For example, Processing has very neat functions for &lt;a rel=&quot;nofollow&quot; href=&quot;https://processing.org/reference/libraries/sound/index.html&quot;&gt;low-pass/high-pass filters&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But for now, we can play sounds - and that's a solid start!&lt;/p&gt;
&lt;h3 id=&quot;references-14&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Processing Sound library documentation: &lt;a rel=&quot;nofollow&quot; href=&quot;https://processing.org/reference/libraries/sound/SoundFile_play_.html&quot;&gt;here&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Divooka Glaze! framework documentation on &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Frameworks/Glaze&quot;&gt;Methodox Wiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>C#</category>
<guid isPermaLink="true">https://coderlegion.com/3324/devlog-20250611-audio-api-design-for-divooka-glaze</guid>
<pubDate>Wed, 11 Jun 2025 18:14:04 +0000</pubDate>
</item>
<item>
<title>TechTalk 20250611: Factory Pattern - Separating Creation from Use</title>
<link>https://coderlegion.com/3322/techtalk-20250611-factory-pattern-separating-creation-from-use</link>
<description>&lt;p&gt;At its core, the &lt;strong&gt;Factory Pattern&lt;/strong&gt; centralizes object creation into dedicated methods or classes, instead of scattering &lt;code&gt;new&lt;/code&gt; calls or complex constructors throughout your codebase. This separation yields several benefits:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Clarity of Intent&lt;/strong&gt;&lt;br&gt;
A method named &lt;code&gt;CreateFromCsv()&lt;/code&gt; or &lt;code&gt;ConfigureNumberDisplay()&lt;/code&gt; immediately tells the reader &lt;em&gt;what&lt;/em&gt; kind of object will emerge, whereas a bare constructor &lt;code&gt;new TextDisplayConfiguration(…)&lt;/code&gt; may require you to look up parameter lists and defaults.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexible Return Types&lt;/strong&gt;&lt;br&gt;
Static factory methods can return any subtype or cached instance they choose. A constructor must always return a fresh instance of its own class.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplified Overloads&lt;/strong&gt;&lt;br&gt;
Instead of dozens of overloaded constructors to cover different initialization scenarios, you provide a handful of well-named static methods (e.g. &lt;code&gt;WithTitle()&lt;/code&gt;, &lt;code&gt;WithSubtitle()&lt;/code&gt;), each encapsulating its own logic and defaults.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validation and Caching&lt;/strong&gt;&lt;br&gt;
Factories can centralize validation (&lt;code&gt;throw&lt;/code&gt; early on bad arguments), perform expensive setup only once (returning a shared singleton), or even pool and reuse objects.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;static-factory-methods-vs-constructors&quot;&gt;Static Factory Methods vs Constructors&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Aspect                          &lt;/th&gt;&lt;th&gt;Constructors                 &lt;/th&gt;&lt;th&gt;Static Factory Methods&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Naming &amp;amp; Discoverability&lt;/strong&gt;    &lt;/td&gt;&lt;td&gt;No name beyond class name    &lt;/td&gt;&lt;td&gt;Method names describe purpose&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Return Flexibility&lt;/strong&gt;          &lt;/td&gt;&lt;td&gt;Fixed to class it belongs to &lt;/td&gt;&lt;td&gt;Can return subtypes or cached refs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Overload Explosion&lt;/strong&gt;          &lt;/td&gt;&lt;td&gt;Many overloads → unreadable  &lt;/td&gt;&lt;td&gt;Distinct methods → self-documented&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Default Configuration Logic&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Scattered in callers         &lt;/td&gt;&lt;td&gt;Encapsulated in one place&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Conditional Instantiation&lt;/strong&gt;   &lt;/td&gt;&lt;td&gt;Hard to vary at runtime      &lt;/td&gt;&lt;td&gt;Easy to branch inside factory&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;divooka-s-use-case-organizing-tools-by-category&quot;&gt;Divooka's Use Case: Organizing Tools by Category&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mz2j271vz39xe3ochc4m.png&quot; alt=&quot;Functions Tray in Divooka - Functions Organized as Categories under Toolboxes&quot;&gt;&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;Divooka&lt;/strong&gt;, every static method in a class becomes a &lt;em&gt;toolbox&lt;/em&gt; entry - with the class name serving as the toolbox &lt;strong&gt;category&lt;/strong&gt;. By grouping related factory methods inside dedicated &lt;code&gt;static class&lt;/code&gt; containers (e.g. &lt;code&gt;PlotConfigurations&lt;/code&gt;), Divooka can automatically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Discover&lt;/strong&gt; all available tools at startup&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Organize&lt;/strong&gt; them under logical headings (one class = one category)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expose&lt;/strong&gt; named methods like &lt;code&gt;ConfigureTextDisplayWithTitleAndSubtitle&lt;/code&gt; directly to end-users&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Were these methods instead constructors on each configuration type, Divooka would lack a natural grouping: you'd have to scan every class for all its constructors, losing that simple &quot;category = class&quot; mapping based on usage.&lt;/p&gt;
&lt;h3 id=&quot;case-study-asp-net-core-s-builder-based-factory-approach&quot;&gt;Case Study: ASP .NET Core's Builder-Based Factory Approach&lt;/h3&gt;
&lt;p&gt;ASP .NET Core also relies heavily on factories - but often via the &lt;strong&gt;builder pattern&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;Host.CreateDefaultBuilder(args)
    .ConfigureServices((ctx, services) =&amp;gt;
    {
        services.AddDbContext&amp;lt;MyDbContext&amp;gt;(opts =&amp;gt; 
            opts.UseSqlServer(connectionString));
        services.AddSingleton&amp;lt;IMyService, MyService&amp;gt;();
    })
    .Build();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here the &quot;factory&quot; is implicit in the &lt;code&gt;IHostBuilder&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fluent configuration&lt;/strong&gt; methods (&lt;code&gt;ConfigureServices&lt;/code&gt;, &lt;code&gt;UseUrls&lt;/code&gt;, etc.) collect options&lt;/li&gt;
&lt;li&gt;The final &lt;code&gt;Build()&lt;/code&gt; call &lt;em&gt;manufactures&lt;/em&gt; the fully wired &lt;code&gt;IHost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;You get immutability until build time, plus a clear sequence of configuration steps&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;comparing-static-vs-builder-factories&quot;&gt;Comparing Static vs. Builder Factories&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Feature             &lt;/th&gt;&lt;th&gt;Static Factory Methods              &lt;/th&gt;&lt;th&gt;Builder Pattern (ASP NET Core)&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Discovery&lt;/strong&gt;       &lt;/td&gt;&lt;td&gt;Simple reflection on static classes &lt;/td&gt;&lt;td&gt;Extension-method based; discoverable by IDE but less obvious at runtime&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Grouping&lt;/strong&gt;        &lt;/td&gt;&lt;td&gt;One class = one category            &lt;/td&gt;&lt;td&gt;Spread across many fluent calls&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Extensibility&lt;/strong&gt;   &lt;/td&gt;&lt;td&gt;Add new methods in classes          &lt;/td&gt;&lt;td&gt;Add new extension methods on interfaces&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Configurability&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Encapsulated per-method logic       &lt;/td&gt;&lt;td&gt;Centralized in a mutable builder state&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Readability&lt;/strong&gt;     &lt;/td&gt;&lt;td&gt;Direct: &lt;code&gt;MyFactory.Create(…)&lt;/code&gt;       &lt;/td&gt;&lt;td&gt;Fluent chaining: &lt;code&gt;builder.Configure…&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In many small‐to‐medium scenarios, &lt;strong&gt;static factory methods&lt;/strong&gt; win on simplicity: you immediately see each creation scenario, and end-users can invoke exactly what they need. In very large frameworks (like ASP .NET Core), the &lt;strong&gt;builder&lt;/strong&gt; shines by aggregating dozens of configuration concerns into a single, coherent pipeline before creating the final product.&lt;/p&gt;
&lt;h3 id=&quot;conclusion-13&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;The Factory Pattern - whether via simple static methods or a full builder API - decouples &lt;em&gt;what&lt;/em&gt; you need from &lt;em&gt;how&lt;/em&gt; it's built. Choosing static factory methods:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keeps creation logic in one readable place&lt;/li&gt;
&lt;li&gt;Leverages method naming to self-document different initialization paths&lt;/li&gt;
&lt;li&gt;Allows frameworks like Divooka to auto-discover and categorize tools&lt;/li&gt;
&lt;li&gt;Avoids constructor overload chaos&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Meanwhile, builder-style factories excel when you have large configuration graphs to assemble in a defined sequence. By understanding both approaches, you can pick the pattern that best balances clarity, flexibility, and discoverability in your own projects.&lt;/p&gt;
</description>
<category>Best Practices</category>
<guid isPermaLink="true">https://coderlegion.com/3322/techtalk-20250611-factory-pattern-separating-creation-from-use</guid>
<pubDate>Wed, 11 Jun 2025 13:28:15 +0000</pubDate>
</item>
<item>
<title>DevLog 20250610: Plotting in Divooka</title>
<link>https://coderlegion.com/3315/devlog-20250610-plotting-in-divooka</link>
<description>&lt;h3 id=&quot;overview-17&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;Plotting is one of those things people take for granted - only when you need it does its absence become painfully obvious. It's often viewed as &quot;solved&quot; - until it's time to implement your own.&lt;/p&gt;
&lt;h3 id=&quot;the-design-principles&quot;&gt;The Design Principles&lt;/h3&gt;
&lt;p&gt;The goal of the plotting API in Divooka is to provide a very high-level, easy-to-use (ideally single-node) setup for common plot types: you just supply the source data, pick the plot type, behold and voila - you get the resulting chart. In the case of the &lt;code&gt;Plotting&lt;/code&gt; toolbox, the results are static images.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kiexzqu3rdj16tp1sijp.png&quot; alt=&quot;IBasic Setup&quot;&gt;&lt;/p&gt;
&lt;p&gt;This scheme allows some fairly complex plot types, as seen here:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8l3itgfzmz2cwodjo220.png&quot; alt=&quot;Population Pyramid Example&quot;&gt;&lt;/p&gt;
&lt;p&gt;However, things can become tricky when we want to support advanced style configurations, for which the convention is to expose data on a node as direct inputs and style configurations as a separate &lt;strong&gt;Configurations&lt;/strong&gt; input, as discussed in &lt;a rel=&quot;nofollow&quot; href=&quot;https://blog.methodox.io/2025/05/13/a-general-service-configuration-scheme-in-graphical-context/&quot;&gt;this blog article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The real challenge is to support creating advanced custom plot types without sacrificing clarity - in which case we have to use OOP under the dataflow (functional programming) constraint:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gxmnhklmlsdiooz7veco.png&quot; alt=&quot;Custom Plot Concept&quot;&gt;&lt;/p&gt;
&lt;p&gt;For convenience and other practical reasons, we also want to support text-based methods like Mermaid - in our case, it's called &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Libraries/Dhole&quot;&gt;Dhole&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mijkwzrhrurma77vzin6.png&quot; alt=&quot;Dhole Setup&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;designing-api-for-custom-plot-types-on-top-of-scottplot&quot;&gt;Designing API for Custom Plot Types on Top of ScottPlot&lt;/h3&gt;
&lt;p&gt;We have chosen &lt;code&gt;ScottPlot&lt;/code&gt; as our underlying drawing backend, and we know it supports a host of &lt;a rel=&quot;nofollow&quot; href=&quot;https://scottplot.net/cookbook/5.0/&quot;&gt;features&lt;/a&gt; - now we need to design a Divooka API or wrapper of some sort so that it exposes the same feature set without exposing underlying ScottPlot types to avoid explicit dependencies.&lt;/p&gt;
&lt;p&gt;ScottPlot 5.0 has a nice compositional API built in:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;ScottPlot.Plot myPlot = new();

myPlot.Add.Signal(Generate.Sin(51));
myPlot.Add.Signal(Generate.Cos(51));

myPlot.Layout.Frameless();
myPlot.DataBackground.Color = Colors.WhiteSmoke;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you can procedurally add elements with object instances:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;ScottPlot.Plot myPlot = new();

ScottPlot.Plottables.LinePlot line = new()
{
    Start = new Coordinates(1, 2),
    End = new Coordinates(3, 4),
};

myPlot.Add.Plottable(line);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One way to create a wrapper library is to create wrappers for all types and Add methods in Divooka:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;// ScottPlot.PlottableAdder
namespace ScottPlot
{
    public class PlottableAdder
    {
        public PlottableAdder(ScottPlot.Plot plot);
        public ScottPlot.Plot Plot { get; }
        public ScottPlot.IPalette Palette { get; set; }
        public ScottPlot.Color GetNextColor(System.Boolean incrementCounter);
        public ScottPlot.Plottables.Annotation Annotation(System.String text, ScottPlot.Alignment alignment);
        public ScottPlot.Plottables.Ellipse AnnularEllipticalSector(ScottPlot.Coordinates center, System.Double outerRadiusX, System.Double outerRadiusY, System.Double innerRadiusX, System.Double innerRadiusY, ScottPlot.Angle startAngle, ScottPlot.Angle sweepAngle, Nullable&amp;lt;ScottPlot.Angle&amp;gt; rotation);
        // ... more methods ...
        public ScottPlot.Plottables.VerticalSpan VerticalSpan(System.Double y1, System.Double y2, Nullable&amp;lt;ScottPlot.Color&amp;gt; color);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which, as you can see above, is a lot.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ScottPlot Plottable Types:
    ScottPlot.IPlottable.cs
    ScottPlot.Plottables.Annotation.cs
    ScottPlot.Plottables.Arrow.cs
    ScottPlot.Plottables.AxisLine.cs
    ScottPlot.Plottables.AxisSpan.cs
    ScottPlot.Plottables.BarPlot.cs
    ScottPlot.Plottables.Benchmark.cs
    ScottPlot.Plottables.BoxPlot.cs
    ScottPlot.Plottables.Bracket.cs
    ScottPlot.Plottables.Callout.cs
    ScottPlot.Plottables.CandlestickPlot.cs
    ...
    ScottPlot.Plottables.VerticalLine.cs
    ScottPlot.Plottables.VerticalSpan.cs
    ScottPlot.Plottables.ZoomRectangle.cs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creating a wrapper for each of those is not too difficult - it's mechanical and can probably be sped up with ChatGPT, but the biggest concern is maintainability and the process can be error-prone during initial setup. A shortcut is to expose the ScottPlot namespace and types directly, since the API already sort of supports &lt;strong&gt;&lt;em&gt;compositional&lt;/em&gt;&lt;/strong&gt; use, but we don't want to create such explicit dependencies.&lt;/p&gt;
&lt;p&gt;A workaround (or middle ground) is that in Divooka we can create a bunch of factory methods and indirectly expose the &lt;code&gt;Plottables&lt;/code&gt; as return values.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;internal class CustomPlot
{
    public record OtherConfigurations(string Title, string XAxisLabel, string YAxisLabel);
    public static PixelImage GeneratePlot(int width, int height, IPlottable[] plottables, OtherConfigurations styles)
    {
        ScottPlot.Plot plot = new();
        foreach (var item in plottables)
            plot.Add.Plottable(item);
        
        if (!string.IsNullOrEmpty(styles.Title))
            plot.Title(styles.Title);
        if (!string.IsNullOrEmpty(styles.XAxisLabel))
            plot.Axes.Bottom.Label.Text = styles.XAxisLabel;
        if (!string.IsNullOrEmpty(styles.YAxisLabel))
            plot.Axes.Left.Label.Text = styles.YAxisLabel;

        return plot.ConvertScottPlotToPixelImage(width, height);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, this is not enough shielding - to vamp it up a level, we just need to define our own &lt;code&gt;Plottable&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public class Plottable
{
    internal IPlottable Underlying;
    public void SetProperty(string attribute, object value)
    {
        // Set actual property of the underlying object...
    }
};

public static PixelImage GeneratePlot(int width, int height, Plottable[] plottables, OtherConfigurations styles)
{
    ScottPlot.Plot plot = new();
    foreach (var item in plottables)
        plot.Add.Plottable(item.Underlying);
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In addition to factory methods, we can provide a bunch of text-based &lt;code&gt;WithXXX&lt;/code&gt; methods to provide a functional re-configuration of data and plottable-specific properties:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public static Plottable WithData(Plottable original, double[] vector)
{
    // ...
}
public static Plottable WithLegendText(Plottable original, string text)
{
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The end result is that we wrote minimal code, avoided having to create a custom wrapper for every plottable, while still largely making use of existing ScottPlot components and exposing a fully functional API in Divooka for custom plotting.&lt;/p&gt;
&lt;h3 id=&quot;example---advanced-plot-customization&quot;&gt;Example - Advanced Plot Customization&lt;/h3&gt;
&lt;p&gt;In this case, we show how to customize a plot with OOP components:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2bnva8wmdbp90rz3fd4j.png&quot; alt=&quot;Full Customizable OOP Functional Dataflow Compositional Plotting API&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;summary-of-available-plots-in-divooka&quot;&gt;Summary of Available Plots in Divooka&lt;/h3&gt;
&lt;p&gt;See table on &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Divooka/Topics/Plotting#summary-of-available-plots&quot;&gt;Wiki page&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;references-15&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Divooka/Topics/Plotting&quot;&gt;Plotting Quick Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/RFC/025&quot;&gt;(Static) Plotting RFC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Libraries/Dhole&quot;&gt;Dhole&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox-technologies.github.io/Website_ParcelOpenStandard/HighLevel_English.html#plotting&quot;&gt;Plotting Library API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Utilities&quot;&gt;Other handy utilities (e.g. QR Code)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://mermaid.js.org/ecosystem/tutorials.html&quot;&gt;Mermaid&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>API Dev</category>
<guid isPermaLink="true">https://coderlegion.com/3315/devlog-20250610-plotting-in-divooka</guid>
<pubDate>Tue, 10 Jun 2025 21:02:05 +0000</pubDate>
</item>
<item>
<title>REST API - A Refresher</title>
<link>https://coderlegion.com/3187/rest-api-a-refresher</link>
<description>&lt;h3 id=&quot;overview-18&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;REST API is the HTTP interface following REST principles to manipulate resources. This quick refresher looks at some common concepts and its application in &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Divooka&quot;&gt;Divooka&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;1-why-you-should-care&quot;&gt;1. Why you should care&lt;/h3&gt;
&lt;p&gt;REST (REpresentational State Transfer) is the de-facto style for web services: roughly 8 out of 10 public APIs you’ll meet follow it, so mastering the basics lets you speak to almost any modern backend.(&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.integrate.io/blog/how-to-make-a-rest-api/?utm_source=chatgpt.com&quot; title=&quot;How to Make a REST API - Integrate.io&quot;&gt;Integrate.io&lt;/a&gt;)&lt;/p&gt;
&lt;h3 id=&quot;2-rest-in-60-seconds&quot;&gt;2. REST in 60 seconds&lt;/h3&gt;
&lt;p&gt;A &lt;strong&gt;REST API&lt;/strong&gt; exposes &lt;em&gt;resources&lt;/em&gt; (nouns such as &lt;code&gt;/users/42&lt;/code&gt;) over plain HTTP. Clients use standard verbs to act on those resources:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Verb       &lt;/th&gt;&lt;th&gt;Typical intent       &lt;/th&gt;&lt;th&gt;Idempotent?&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;GET&lt;/strong&gt;    &lt;/td&gt;&lt;td&gt;Read                 &lt;/td&gt;&lt;td&gt;✔︎&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;POST&lt;/strong&gt;   &lt;/td&gt;&lt;td&gt;Create / adj. action &lt;/td&gt;&lt;td&gt;✖︎&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;PUT&lt;/strong&gt;    &lt;/td&gt;&lt;td&gt;Replace              &lt;/td&gt;&lt;td&gt;✔︎&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;PATCH&lt;/strong&gt;  &lt;/td&gt;&lt;td&gt;Partial update       &lt;/td&gt;&lt;td&gt;✖︎&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;DELETE&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Remove               &lt;/td&gt;&lt;td&gt;✔︎&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;RESTful servers must also follow six architectural constraints (uniform interface, client-server, stateless, cacheable, layered, &lt;em&gt;code on demand&lt;/em&gt; optional).(&lt;a rel=&quot;nofollow&quot; href=&quot;https://restfulapi.net/?utm_source=chatgpt.com&quot; title=&quot;REST API Tutorial: What is REST?&quot;&gt;restfulapi.net&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.integrate.io/blog/how-to-make-a-rest-api/?utm_source=chatgpt.com&quot; title=&quot;How to Make a REST API - Integrate.io&quot;&gt;Integrate.io&lt;/a&gt;)&lt;/p&gt;
&lt;h3 id=&quot;3-common-response-codes&quot;&gt;3. Common response codes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;200 OK / 201 Created&lt;/strong&gt; – success&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;400 Bad Request&lt;/strong&gt; – client error (malformed JSON, missing fields)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;401/403 Unauthorized / Forbidden&lt;/strong&gt; – auth problems&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;404 Not Found&lt;/strong&gt; – wrong URI or missing record&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;500+ Server errors&lt;/strong&gt; – bug, outage, etc.(&lt;a rel=&quot;nofollow&quot; href=&quot;https://blog.postman.com/rest-api-examples/?utm_source=chatgpt.com&quot; title=&quot;What Is a REST API? Examples, Uses &amp;amp; Challenges - Postman Blog&quot;&gt;Postman Blog&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;4-tiny-hello-world-example&quot;&gt;4. Tiny “Hello World” example&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;GET https://api.example.com/items/123
Accept: application/json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Typical JSON reply:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;id&quot;: 123,
  &quot;name&quot;: &quot;Widget&quot;,
  &quot;price&quot;: 9.99
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;5-trying-rest-in-divooka&quot;&gt;5. Trying REST in &lt;strong&gt;Divooka&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Divooka lets you build the call as a &lt;strong&gt;node graph&lt;/strong&gt; instead of filling long forms:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Send (HTTP) Request&lt;/strong&gt; node – set &lt;strong&gt;GET&lt;/strong&gt; and URL &lt;code&gt;{{baseUrl}}/items/{{itemId}}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Headers&lt;/strong&gt; – drop a &lt;strong&gt;Custom Header&lt;/strong&gt; node for key:value pairs like &lt;code&gt;Accept: application/json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;(Optional) &lt;strong&gt;Auth node&lt;/strong&gt; – choose Basic, Bearer, or OAuth 2.0.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preview JSON&lt;/strong&gt; node to parse JSON payloads – pretty-prints and type-checks the body.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Everything is visual and reusable: connect the same response into further logic (save to DB, update UI, trigger another request) without writing glue code.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://publication.tor1.cdn.digitaloceanspaces.com/Websites/MethodoxWiki/Images/Divooka-AppDevelopment-APITesting_Usage.png&quot; alt=&quot;Usage screenshot&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can view more practical examples on the &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/Divooka/AppDevelopment/APITesting&quot;&gt;Web API Testing&lt;/a&gt; page.&lt;/p&gt;
&lt;h4 id=&quot;environment-variables&quot;&gt;Environment variables&lt;/h4&gt;
&lt;p&gt;Define &lt;code&gt;baseUrl&lt;/code&gt;, &lt;code&gt;apiKey&lt;/code&gt;, or &lt;code&gt;authToken&lt;/code&gt; once in &lt;strong&gt;Environments&lt;/strong&gt; and reference them anywhere with &lt;strong&gt;Get Environment Variable&lt;/strong&gt; nodes. Switch environments (dev/staging/prod) using &lt;strong&gt;Switch&lt;/strong&gt; node.&lt;/p&gt;
&lt;h4 id=&quot;saving-amp-sharing&quot;&gt;Saving &amp;amp; Sharing&lt;/h4&gt;
&lt;p&gt;Save and group related requests into graphs as documents, so it can be easily reused and shared.&lt;/p&gt;
&lt;h3 id=&quot;6-best-practice-checklist&quot;&gt;6. Best-practice checklist&lt;/h3&gt;
&lt;p&gt;For designing your own REST APIs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Keep URIs noun-based&lt;/strong&gt; (&lt;code&gt;/orders/17&lt;/code&gt;, not &lt;code&gt;/getOrder&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use correct verbs&lt;/strong&gt; (no &lt;code&gt;GET /deleteUser&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Surface pagination &amp;amp; filtering (&lt;code&gt;?page=2&amp;amp;limit=50&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Version in the path or header (&lt;code&gt;/v1/&lt;/code&gt;), never break existing clients.&lt;/li&gt;
&lt;li&gt;Secure with HTTPS, auth, and sensible rate limits.&lt;/li&gt;
&lt;li&gt;Document with &lt;strong&gt;OpenAPI&lt;/strong&gt;; Divooka can import the spec and scaffold nodes automatically.(&lt;a rel=&quot;nofollow&quot; href=&quot;https://idratherbewriting.com/learnapidoc/docapis_what_is_a_rest_api.html?utm_source=chatgpt.com&quot; title=&quot;What is a REST API? | I'd Rather Be Writing Blog and API doc course&quot;&gt;idratherbewriting.com&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;7-further-reading&quot;&gt;7. Further reading&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Usage documentation in &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/Divooka/AppDevelopment/APITesting&quot;&gt;Divooka&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Roy Fielding’s &lt;a rel=&quot;nofollow&quot; href=&quot;https://ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation.pdf&quot;&gt;REST dissertation&lt;/a&gt; (the original source)&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc9110.html&quot;&gt;RFC 9110&lt;/a&gt; – HTTP Semantics&lt;/li&gt;
&lt;li&gt;Postman “REST API examples” blog for real-world patterns(&lt;a rel=&quot;nofollow&quot; href=&quot;https://blog.postman.com/rest-api-examples/?utm_source=chatgpt.com&quot; title=&quot;What Is a REST API? Examples, Uses &amp;amp; Challenges - Postman Blog&quot;&gt;Postman Blog&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Divooka Wiki → &lt;strong&gt;App Development &amp;gt; API Testing&lt;/strong&gt; (deep-dive on every node and feature)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;With these essentials you can explore any REST endpoint confidently - and wire it into your Divooka flows to suit your application needs.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Web Dev</category>
<guid isPermaLink="true">https://coderlegion.com/3187/rest-api-a-refresher</guid>
<pubDate>Mon, 02 Jun 2025 05:36:19 +0000</pubDate>
</item>
<item>
<title>Hello World in Divooka - Basic Generative AI Program</title>
<link>https://coderlegion.com/3185/hello-world-in-divooka-basic-generative-ai-program</link>
<description>&lt;h2 id=&quot;hello-world&quot;&gt;Hello World&lt;/h2&gt;
&lt;p&gt;Divooka™ is a node‑based visual programming environment that lets you build workflows by connecting functional blocks (called &lt;em&gt;nodes&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;In this example, we are going to see a simple but fully functional program in Divooka™. We are going to ask ChatGPT (from OpenAI) to generate a text prompt for an image generator, generate an image, and then preview it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://publication.tor1.cdn.digitaloceanspaces.com/Websites/MethodoxWiki/Images/Divooka_Start_Hello_Setup.png&quot; alt=&quot;Node Connection Setup&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;A working installation of Divooka™. All functions mentioned in this example is available in standard Divooka™ Explore distribution.&lt;/li&gt;
&lt;li&gt;An OpenAI API key configured in Divooka™’s AI Services toolbox. You can use &lt;strong&gt;Configure OpenAI&lt;/strong&gt; node or simply save an environment variable &lt;code&gt;OPENAI_KEY&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Basic familiarity with Divooka™’s interface (e.g., dragging nodes, wiring connections).&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;building-the-graph&quot;&gt;Building the Graph&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://publication.tor1.cdn.digitaloceanspaces.com/Websites/MethodoxWiki/Images/Divooka-DataAnalytics_Canonical_PickTool.png&quot; alt=&quot;Pick tool to use&quot;&gt;&lt;/p&gt;
&lt;p&gt;To build the above program, use the following nodes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Chat GPT Complete&lt;/strong&gt; under &lt;strong&gt;OpenAI Helper&lt;/strong&gt; (You can find it in the &lt;strong&gt;AI Services&lt;/strong&gt; toolbox)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generate Image&lt;/strong&gt; under &lt;strong&gt;OpenAI Helper&lt;/strong&gt; category (You can find it in the &lt;strong&gt;AI Services&lt;/strong&gt; toolbox)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preview&lt;/strong&gt; from the &lt;strong&gt;Basic&lt;/strong&gt; toolbox&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;running-the-program&quot;&gt;Running the Program&lt;/h3&gt;
&lt;p&gt;To execute the program, click on the little &lt;strong&gt;&quot;Run&quot;&lt;/strong&gt; button at the upper right corner of the &lt;strong&gt;Preview&lt;/strong&gt; node.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://publication.tor1.cdn.digitaloceanspaces.com/Websites/MethodoxWiki/Images/Divooka_Start_Hello_Result.png&quot; alt=&quot;Execution Result&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-happened-&quot;&gt;What Happened?&lt;/h3&gt;
&lt;p&gt;We've asked Chat GPT to &lt;em&gt;&quot;help me generate a prompt with about 300 words for an image generator (in this case Dall E 3) - describe an interesting sci-fi landscape&quot;&lt;/em&gt;, and used its output as input to the image generator.&lt;/p&gt;
&lt;p&gt;This simple example illustrates several basic concepts of using Divooka™:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Functions are available from toolboxes; they are further organized using categories. Certain functions may have variations that differ slightly in behavior.&lt;/li&gt;
&lt;li&gt;You can connect inputs and outputs between nodes by holding and dragging the left mouse button. That forms the logic flow of the program.&lt;/li&gt;
&lt;li&gt;To run a program, click the &lt;strong&gt;Run&lt;/strong&gt; button on the final node from which you wish to obtain results.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;configure-nodes&quot;&gt;Configure Nodes&lt;/h3&gt;
&lt;p&gt;If you want to pick a different model, or specifying API Key explicitly, you can use the &lt;strong&gt;Configure OpenAI&lt;/strong&gt; node.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://publication.tor1.cdn.digitaloceanspaces.com/Websites/MethodoxWiki/Images/Divooka-DataAnalytics_Canonical_Configure.png&quot; alt=&quot;Configure OpenAI&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;remarks&quot;&gt;Remarks&lt;/h3&gt;
&lt;p&gt;Congratulations! You've built your first fully functional and useful Divooka™ program! Embark on an amazing journey and enjoy an unprecedented programming experience with Divooka™. As you learn more about what's available, you will be able to build more and more powerful Divooka™ programs with more sophisticated node setups.&lt;/p&gt;
</description>
<category>Getting Started</category>
<guid isPermaLink="true">https://coderlegion.com/3185/hello-world-in-divooka-basic-generative-ai-program</guid>
<pubDate>Sun, 01 Jun 2025 23:21:14 +0000</pubDate>
</item>
<item>
<title>A General Service Configuration Scheme in Graphical Context</title>
<link>https://coderlegion.com/3150/a-general-service-configuration-scheme-in-graphical-context</link>
<description>&lt;h3 id=&quot;overview-19&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;In this article, we take a look at one emerging pattern that provides a straightforward and compact way to configure services. Generally speaking, when a function expects many inputs, the most straightforward way is to directly expose those on the node. However, this quickly makes the node gigantic in size.&lt;/p&gt;
&lt;p&gt;(Node with many parameters)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=10966338511247207470&quot; alt=&quot;Node with many parameters&quot;&gt;&lt;/p&gt;
&lt;p&gt;(Example in ComfyUI)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=4213407889948385493&quot; alt=&quot;Example in ComfyUI&quot;&gt;&lt;/p&gt;
&lt;p&gt;When a node has too many parameters, it becomes bulky.&lt;/p&gt;
&lt;p&gt;(Example in Blender)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=11621986877267314345&quot; alt=&quot;Example in Blender&quot;&gt;&lt;/p&gt;
&lt;p&gt;This quickly becomes infeasible when even more complex parameters are required for the functioning of nodes—for instance, if it's an online service with many potential configuration settings. A typical approach is thus to utilize a GUI element, which we shall call a &quot;node properties panel.&quot; Below is a sophisticated example from Houdini. PowerBI and Zapier do similar things.&lt;/p&gt;
&lt;p&gt;(Houdini node configuration panel)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=15805477494334374509&quot; alt=&quot;Houdini node configuration panel&quot;&gt;&lt;/p&gt;
&lt;p&gt;This method falls short for two reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It's not explicit, and configuration parameters are not visible on the graph. Which makes it not possible to see dataflow nor to programmatically drive those values.&lt;/li&gt;
&lt;li&gt;It requires a dedicated GUI and can only be configured within that GUI.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;the-design-approach&quot;&gt;The Design Approach&lt;/h3&gt;
&lt;p&gt;Usually, some kind of scripting or expression language is used to address the first problem. For instance, in Houdini, users often write VEX snippets or Python expressions inside parameter fields to control behavior dynamically. In Zapier, configuration can include custom JavaScript code or formulas in input fields to manipulate data between steps. These workarounds bring back some level of flexibility, but at the cost of breaking the visual flow and requiring users to write code inside otherwise &quot;no-code&quot; or &quot;low-code&quot; environments.&lt;/p&gt;
&lt;p&gt;One design goal of Divooka is to be frontend-agnostic. The currently released frontend is officially known as &quot;Neo&quot;, which is a WPF-based technology. However, Divooka graphs are designed to be generic enough to be visualized on different frontends—ideally in a way that's very easy to implement. That’s why we prefer to expose everything explicitly, so no specialized logic is required on the frontend (e.g., to be aware of the nodes they are dealing with).&lt;/p&gt;
&lt;h3 id=&quot;configuration-in-divooka&quot;&gt;Configuration in Divooka&lt;/h3&gt;
&lt;p&gt;Visually, we have a &lt;code&gt;ConfigureX&lt;/code&gt; node and some nodes that take a configuration parameter as input. This dominant pattern is used in many places, including plotting configuration, OpenAI service configuration, and some other APIs like the image composition API.&lt;/p&gt;
&lt;p&gt;(Example of plot configuration)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=17498224958783455849&quot; alt=&quot;Example of plot configuration&quot;&gt;&lt;/p&gt;
&lt;p&gt;(Example of OpenAI service configuration)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=8094236953975528394&quot; alt=&quot;Example of OpenAI service configuration&quot;&gt;&lt;/p&gt;
&lt;p&gt;(Example of image composition API)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=10649681161302771571&quot; alt=&quot;Example of image composition API&quot;&gt;&lt;/p&gt;
&lt;p&gt;We could provide a few different overrides for creating a configuration—so depending on how many details are needed, one can use a more lightweight or more heavy-duty configure node.&lt;/p&gt;
&lt;p&gt;(PostgreSQL with two different configurations)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=6301383600821675371&quot; alt=&quot;PostgreSQL with two different configurations&quot;&gt;&lt;/p&gt;
&lt;p&gt;That concludes our introduction to the current setup, but the versatility of Divooka doesn't end here. Indeed, we could also introduce some kind of GUI panels for advanced configurations, and in fact, that’s desirable for certain things - at the price of losing the capability to programmatically drive the parameter values. This will be an expected standard feature in the full release of Divooka.&lt;/p&gt;
&lt;h3 id=&quot;references-16&quot;&gt;References&lt;/h3&gt;
&lt;p&gt;This article reference the following software:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Houdini – A professional 3D animation and visual effects software used in film, TV, and games, known for its node-based procedural workflow.&lt;/li&gt;
&lt;li&gt;Blender – A free and open-source 3D creation suite that supports modeling, animation, simulation, rendering, and more.&lt;/li&gt;
&lt;li&gt;ComfyUI – A graphical node-based interface for building image generation workflows using AI models like Stable Diffusion.&lt;/li&gt;
&lt;li&gt;PowerBI – A business analytics tool by Microsoft that lets users visualize data and share insights across an organization.&lt;/li&gt;
&lt;li&gt;PostgreSQL – A powerful, open-source relational database system with a strong emphasis on extensibility and standards compliance.&lt;/li&gt;
&lt;li&gt;Divooka – A general purpose programming language for building procedural programs and data flows through node graphs.&lt;/li&gt;
&lt;li&gt;Zapier – An online automation platform that connects different apps and services to automate workflows without coding.&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Best Practices</category>
<guid isPermaLink="true">https://coderlegion.com/3150/a-general-service-configuration-scheme-in-graphical-context</guid>
<pubDate>Sun, 25 May 2025 06:16:54 +0000</pubDate>
</item>
<item>
<title>DevLog 20250510 Dealing with Lambda</title>
<link>https://coderlegion.com/3065/devlog-20250510-dealing-with-lambda</link>
<description>&lt;p&gt;(The technical term is &quot;Closure&quot;)&lt;/p&gt;
&lt;p&gt;The essence of lambda calculus is with captures - be it simple values or object references. At the site of anonymous function declaration, it's being captured and (the reference) is saved inside the lambda until being invoked.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// C# – Capturing a local variable in a LINQ query
int factor = 3;
int[] numbers = { 1, 2, 3, 4, 5 };
var scaled = numbers
    .Select(n =&amp;gt; n * factor)   // ‘factor’ is captured from the enclosing scope
    .ToArray();
Console.WriteLine(string.Join(&quot;, &quot;, scaled));  // 3, 6, 9, 12, 15
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In C++ this is more explicit, and the capturing process is more obvious:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// C++20 – Capturing a local variable in a ranges pipeline
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;ranges&amp;gt;

int main() {
    int factor = 3;
    std::vector&amp;lt;int&amp;gt; numbers = { 1, 2, 3, 4, 5 };

    // Capture ‘factor’ by value in the lambda
    auto scaled = numbers 
        | std::views::transform([factor](int n) { return n * factor; });

    for (int x : scaled)
        std::cout &amp;lt;&amp;lt; x &amp;lt;&amp;lt; &quot; &quot;;   // 3 6 9 12 15
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What happens when an object reference is disposed, as in the case of &lt;code&gt;IDisposable&lt;/code&gt;? It will simply throw an error.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System;
using System.IO;

class Program
{
    static void Main()
    {
        // Create and use a MemoryStream
        var ms = new MemoryStream();
        ms.WriteByte(0x42);                // OK: writes a byte

        // Dispose the stream
        ms.Dispose();                      // Unmanaged buffer released

        try
        {
            // Any further operation is invalid
            ms.WriteByte(0x24);            // &amp;lt;-- throws ObjectDisposedException
        }
        catch (ObjectDisposedException ex)
        {
            Console.WriteLine($&quot;Cannot use disposed object: {ex.GetType().Name}&quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c87yyhj6tqyth0sf3ddi.png&quot; alt=&quot;IDisposable capture runtime exception&quot;&gt;&lt;/p&gt;
&lt;p&gt;An important distinction is events or plain callbacks that requires no return value, which can be implemented quite plainly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/irt8ep76g8kibc5tb1hd.png&quot; alt=&quot;Divooka procedural context custom event&quot;&gt;&lt;/p&gt;
&lt;p&gt;To achieve the same in a dataflow context, some kind of GUI (or &quot;graph-native&quot;) support is needed, and to the caller, it's clear (in the language of C#) it's taking a delegate as argument, as in the case of LINQ &lt;code&gt;Select&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0j8mzw6jfcxnlplhgx4i.png&quot; alt=&quot;Example of &amp;quot;Apply&amp;quot; with delegate input&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static System.Collections.Generic.IEnumerable&amp;lt;TResult&amp;gt; Select&amp;lt;TSource,TResult&amp;gt;(this System.Collections.Generic.IEnumerable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource,int,TResult&amp;gt; selector);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To implement capturing, the most natural way is to ensure it happens &quot;in-place&quot; - directly on the graph. With this approach, we don’t need specialized nodes for every kind of function; we can instead rely on existing language constructs to handle the rest.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4minad52ehm5to3j26kk.png&quot; alt=&quot;Final implementation&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5mfu3m8xwtqj42icd6yc.png&quot; alt=&quot;Demo Function&quot;&gt;&lt;/p&gt;
&lt;p&gt;The last bit is actually inspired by Haskell, where functions are first-class and can be composed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- A simple two‑argument function
add :: Int -&amp;gt; Int -&amp;gt; Int
add x y = x + y

-- Partially apply 'add' to “capture” the first argument
addFive :: Int -&amp;gt; Int
addFive = add 5

main :: IO ()
main = do
  print (addFive 10)          -- 15
  print (map (add 3) [1,2,3]) -- [4,5,6]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See a demo of usage &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/methodoxdivooka/progress-share-graph-local-lambda-calculus-4dj2&quot;&gt;here&lt;/a&gt;, reposted below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/npdq24nat7hrusu0qxrd.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
</description>
<category>C#</category>
<guid isPermaLink="true">https://coderlegion.com/3065/devlog-20250510-dealing-with-lambda</guid>
<pubDate>Fri, 16 May 2025 19:49:54 +0000</pubDate>
</item>
<item>
<title>Reflections on My Second LangChain Session: Hype, Substance, and Agentic Potential</title>
<link>https://coderlegion.com/3064/reflections-on-my-second-langchain-session-hype-substance-and-agentic-potential</link>
<description>&lt;p&gt;Today marked my second time attending a LangChain talk. My first was at a previous company, and this time, I joined an ACM-invited session. Unfortunately, my impression hasn’t changed much. Like the last time, the presentation leaned more toward a marketing pitch than a meaningful deep-dive into technology.&lt;/p&gt;
&lt;p&gt;Forty minutes into the session, we were still swimming in vague promises and general problem statements. &quot;LangGraph can solve this,&quot; they said - without ever really explaining how.&lt;/p&gt;
&lt;h3 id=&quot;what-langchain-brings-to-the-table&quot;&gt;What LangChain Brings to the Table&lt;/h3&gt;
&lt;p&gt;To be fair, LangChain does offer a decent toolbox. Its ecosystem includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Paid utilities (like PDF ingestion)&lt;/li&gt;
&lt;li&gt;Core components such as text splitters, output parsers, document loaders, and vector stores&lt;/li&gt;
&lt;li&gt;Compositional elements like prompts, example selectors, tools, and model abstractions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Among these, the PDF ingestion tool stood out. It's genuinely useful - just not particularly accessible, since it sits behind a paywall. That pattern of paywalled features brings up the classic vendor lock-in concern. When a tech stack becomes too reliant on a single platform, especially one with closed components, you're playing a risky game. And to be blunt, the overall design doesn’t feel groundbreaking.&lt;/p&gt;
&lt;h3 id=&quot;learning-by-observing&quot;&gt;Learning by Observing&lt;/h3&gt;
&lt;p&gt;For me, the purpose of attending wasn't just about learning the tool - it was about observing how they package and present AI service integration. As Divooka continues to evolve and offer deeper native AI capabilities, I find myself increasingly valuing compositional and node-native approaches, like what ComfyUI offers. Its visual chaining model feels more transparent and modular - something that can be critical when building robust, production-grade AI workflows.&lt;/p&gt;
&lt;p&gt;That said, there &lt;em&gt;was&lt;/em&gt; one key idea from the LangChain session that resonated: agents &lt;em&gt;do&lt;/em&gt; matter. Agentic applications, when structured with purpose and supported by strong pipelines, can lead to some powerful real-world use cases.&lt;/p&gt;
&lt;h3 id=&quot;the-real-time-use-challenge&quot;&gt;The &quot;Real-Time Use&quot; Challenge&lt;/h3&gt;
&lt;p&gt;The first problem they introduced had to do with real-time responsiveness. Ironically, they later conceded it wasn’t really a &quot;problem&quot; but more a matter of user-perceived latency. Still, here's how they broke it down:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;strong&gt;Challenge&lt;/strong&gt;                                           &lt;/th&gt;&lt;th&gt;&lt;strong&gt;Proposed Solution&lt;/strong&gt;&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Multiple LLM calls needed                               &lt;/td&gt;&lt;td&gt;Parallelize steps where possible&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Non-LLM steps (e.g., RAG, database queries, tool calls) &lt;/td&gt;&lt;td&gt;Parallelize or batch when feasible&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Keeping users engaged during waiting periods            &lt;/td&gt;&lt;td&gt;Stream intermediate outputs (optional)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The advice wasn’t necessarily wrong - but it felt narrow. These solutions are tailored for real-time UIs and don’t necessarily tap into the deeper promise of agentic systems. Agents shouldn’t just be about speed - they should be about capability, reasoning, adaptability.&lt;/p&gt;
&lt;h3 id=&quot;langgraph-intriguing-promises-lingering-questions&quot;&gt;LangGraph: Intriguing Promises, Lingering Questions&lt;/h3&gt;
&lt;p&gt;LangGraph, as an extension of LangChain, introduces some genuinely interesting features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Persistent, long-running workflows with streaming and human-in-the-loop support&lt;/li&gt;
&lt;li&gt;Integration with LangSmith for observability&lt;/li&gt;
&lt;li&gt;A main graph architecture augmented by subgraphs for each agent - supporting multi-agent interaction patterns&lt;/li&gt;
&lt;li&gt;Native support for both token- and event-level streaming&lt;/li&gt;
&lt;li&gt;(Interestingly, someone compared it to Amazon Bedrock Flows, which seems like a fair analogy)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Despite the potential, several critical questions remain unanswered:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What does the full system architecture look like?&lt;/li&gt;
&lt;li&gt;Is LangGraph something you &lt;em&gt;must&lt;/em&gt; self-host, or does it assume reliance on LangChain’s hosted infrastructure?&lt;/li&gt;
&lt;li&gt;How is state managed internally, and how configurable is the runtime model?&lt;/li&gt;
&lt;li&gt;What’s the actual developer experience like when setting up human-in-the-loop interaction?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They did recommend a textbook - &lt;em&gt;Learning LangChain: Building AI and LLM Applications with LangChain and LangGraph&lt;/em&gt; by Mayo and Nuno - which might offer some clarity. But relying on a textbook to fill in architectural gaps from a talk feels like a missed opportunity.&lt;/p&gt;
&lt;h3 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;These sessions are always worth attending - not necessarily for the content, but for the context. It’s important to see how the broader ecosystem evolves, what narratives companies are pushing, and where we can differentiate.&lt;/p&gt;
&lt;p&gt;At Methodox, we’re focusing on flexibility, transparency, and seamless integration across AI services. That’s why our approach to Divooka aligns more closely with frameworks like ComfyUI - ones that embrace openness and modularity.&lt;/p&gt;
&lt;p&gt;The future of AI applications will depend not just on speed or flashy demos, but on clarity of architecture, trust in execution, and real-world adaptability. That’s where I believe we can lead.&lt;/p&gt;
</description>
<category>Data Science</category>
<guid isPermaLink="true">https://coderlegion.com/3064/reflections-on-my-second-langchain-session-hype-substance-and-agentic-potential</guid>
<pubDate>Fri, 16 May 2025 16:43:56 +0000</pubDate>
</item>
</channel>
</rss>