<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>f20x</title><description>a blog about random web development topics</description><link>https://f20x.com</link><item><title>Demystifying the Random Avatar Generator: A JavaScript Code Analysis</title><link>https://f20x.com/posts/random-pattern-generator</link><guid isPermaLink="true">https://f20x.com/posts/random-pattern-generator</guid><description>A beginner-friendly breakdown of the JavaScript patterns behind a random avatar generator.</description><pubDate>Thu, 23 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Have you ever wondered how websites generate those unique, pixelated default profile pictures for new users? They might look like simple geometric shapes, but under the hood, there is a fascinating mix of mathematics, software design, and web technologies at play.&lt;/p&gt;
&lt;p&gt;This article analyzes a specific open-source library, the &lt;a href=&quot;https://github.com/fractalsoftware/random-avatar-generator&quot;&gt;&lt;strong&gt;Random Avatar Generator&lt;/strong&gt;&lt;/a&gt;, created by &lt;em&gt;Fractal Software&lt;/em&gt;. Designed as part of an academic publication and a side project, this library serves an educational purpose: to illustrate different aspects of JavaScript&apos;s capabilities and general software development concepts.&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;fractalsoftware/random-avatar-generator&quot;}&lt;/p&gt;
&lt;p&gt;The tool generates random pixel-pattern avatars with a focus on &lt;strong&gt;low collision&lt;/strong&gt; (ensuring you rarely get the same avatar twice) and &lt;strong&gt;zero external dependencies&lt;/strong&gt; (meaning it doesn&apos;t rely on massive third-party libraries). Let&apos;s dive into the patterns, programming aspects, and computer science concepts this elegant script employs.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Separation of Concerns (Architecture)&lt;/h2&gt;
&lt;p&gt;A fundamental principle in computer science is &lt;strong&gt;Separation of Concerns (SoC)&lt;/strong&gt;—dividing a computer program into distinct sections so each section addresses a separate concern. In this library, the creator explicitly divided the avatar creation process into two distinct phases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Data Generation&lt;/strong&gt; (&lt;code&gt;generateRandomAvatarData&lt;/code&gt;): This method computes the math and logic to randomly determine which &quot;pixels&quot; are turned on or off and what colors they should be. It outputs a lightweight string of data (e.g., &lt;code&gt;0-6-6te25-9d9p0-xd5g&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rendering&lt;/strong&gt; (&lt;code&gt;getAvatarFromData&lt;/code&gt;): This method takes the data string and translates it into an actual visual graphic (an SVG image).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Why is this a good pattern?&lt;/strong&gt; Because the application state (the data string) is separate from the user interface (the SVG). If you want to save a user&apos;s generated avatar in a database, you don&apos;t need to save a massive image file; you just save the tiny string &lt;code&gt;0-6-6te25-9d9p0-xd5g&lt;/code&gt; and re-render it whenever needed! To make things easier for developers who just want a quick image, the author also includes a helper function called &lt;code&gt;getRandomAvatar()&lt;/code&gt; that combines both steps.&lt;/p&gt;
&lt;h2&gt;2. The Strategy Pattern for Rendering&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;Strategy Pattern&lt;/strong&gt; is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate function, and make their objects interchangeable.&lt;/p&gt;
&lt;p&gt;The library includes an API parameter called &lt;code&gt;renderMethod&lt;/code&gt;. By default, the avatar uses a &lt;code&gt;square&lt;/code&gt; rendering strategy, but developers can change it to a &lt;code&gt;circle&lt;/code&gt; strategy. Even better, the library accepts a &lt;strong&gt;custom callback function&lt;/strong&gt; to draw entirely new shapes.&lt;/p&gt;
&lt;p&gt;For example, a developer can pass a custom function to draw triangles instead of squares:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const drawTriangle = (resolution, indexX, indexY) =&amp;gt; {
   return `M${indexX * resolution + resolution / 2},${indexY * resolution} l${ resolution / 2 } ${resolution} l-${resolution} 0z`;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a beginner, this is a prime example of &lt;strong&gt;Polymorphism&lt;/strong&gt; and &lt;strong&gt;High Extensibility&lt;/strong&gt;. The core engine of the library doesn&apos;t need to know &lt;em&gt;how&lt;/em&gt; to draw a triangle; it just delegates the drawing logic to whatever function you provide it.&lt;/p&gt;
&lt;h2&gt;3. Dealing with &quot;Collision&quot; (Probability and Math)&lt;/h2&gt;
&lt;p&gt;In computer science, a &quot;collision&quot; happens when two different inputs produce the exact same output. In the context of an avatar generator, a collision means two different users randomly getting the exact same profile picture.&lt;/p&gt;
&lt;p&gt;To solve this, the script relies heavily on matrix generation and probability manipulation. According to the documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&quot;In order to have bigger numeric space to prevent collision, each row within the matrix has a random color number.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;By associating randomization not just with the coordinates (X and Y axes), but also injecting color variation at the row level, the total permutations (the number of possible unique avatars) skyrocket. The strings generated (like &lt;code&gt;6te25&lt;/code&gt;) represent integers converted into alphanumeric base formats (like base-36 or hexadecimal). Base conversion is a highly effective way to compress large numerical values into short, readable strings.&lt;/p&gt;
&lt;h2&gt;4. Scalable Vector Graphics (SVG) and Template Literals&lt;/h2&gt;
&lt;p&gt;Instead of using an HTML Canvas element or generating an image file (like a PNG or JPG), the script generates &lt;strong&gt;SVG (Scalable Vector Graphics)&lt;/strong&gt; code.&lt;/p&gt;
&lt;p&gt;SVG is an XML-based markup language used for describing two-dimensional vector graphics. Since SVGs are built using mathematical formulas (lines, paths, and curves) instead of fixed pixels, they can scale to any size without losing quality.&lt;/p&gt;
&lt;p&gt;The library uses modern JavaScript &lt;strong&gt;Template Literals&lt;/strong&gt; (strings wrapped in backticks &lt;code&gt;`&lt;/code&gt;) to dynamically inject variables directly into the SVG path strings:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;`&amp;lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;100%&quot; height=&quot;100%&quot; viewBox=&quot;0 0 ${size} ${size}&quot;&amp;gt;...`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For entry-level developers, manipulating DOM elements or drawing directly via JavaScript can be cumbersome. Building a raw markup string and returning it is often a faster, more flexible approach that works identically on a Web Browser, inside a Node.js server, or inside a React component.&lt;/p&gt;
&lt;h2&gt;5. Functional Default Parameters&lt;/h2&gt;
&lt;p&gt;Another modern JavaScript concept used across the library&apos;s API is the use of &lt;strong&gt;Default Parameters&lt;/strong&gt;. Functions like &lt;code&gt;generateRandomAvatarData&lt;/code&gt; and &lt;code&gt;getAvatarFromData&lt;/code&gt; have fallback values if the user decides not to configure them.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;complexity&lt;/code&gt;: Defaults to &lt;code&gt;16&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;renderMethod&lt;/code&gt;: Defaults to &lt;code&gt;&quot;square&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;size&lt;/code&gt;: Defaults to &lt;code&gt;256&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This represents a design philosophy called &lt;strong&gt;Convention over Configuration&lt;/strong&gt;. It means the library works flawlessly out of the box with sensible defaults, while still allowing power-users to open the hood and tinker with the parameters.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Playground&lt;/h2&gt;
&lt;p&gt;You can see this application in action, an generate your own patterns here: &lt;a href=&quot;https://morra.co/random-avatar-generator/&quot;&gt;Random Avatar Generator&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;What appears to be a simple script for rendering colorful boxes is actually a rich educational playground. By reading through scripts like the Random Avatar Generator, computer science students and junior developers can see practical implementations of architectural decoupling, base-conversions, strategy patterns, and clever utilization of native web capabilities like SVG. Next time you encounter a randomly generated default avatar, you&apos;ll know exactly what kind of software magic is happening behind the scenes!&lt;/p&gt;
</content:encoded><author>Manuel Herrera</author></item><item><title>How (and why) I vibe-coded an app to test Markov chains for image generation</title><link>https://f20x.com/posts/markov-chain-vibe-coded-app</link><guid isPermaLink="true">https://f20x.com/posts/markov-chain-vibe-coded-app</guid><description>Exploring how to build fast applications with modern AI tools. A personal approach.</description><pubDate>Tue, 05 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Some days ago, binge-watching on YouTube, I &quot;found&quot; the latest video from the famous channel &lt;a href=&quot;https://www.youtube.com/@veritasium&quot;&gt;Veritasium&lt;/a&gt; about the mathematical concept called Markov chains. Of course, its catchy (clickbait) title, &lt;a href=&quot;https://www.youtube.com/watch?v=KZeIEiBrT_w&quot;&gt;The Strange Math That Predicts (Almost) Anything&lt;/a&gt;, engaged me immediately. After half an hour engaged by its storytelling and well produced content, I wanted to test the concepts exposed there.&lt;/p&gt;
&lt;p&gt;I must admit that I believed that, before watching that video, I understood what is a Markov chain. Wrong. The simplicity of the math behind left me surprised and fully motivated to experiment in a field that I always find exciting: image manipulation.&lt;/p&gt;
&lt;h2&gt;A proof of concept&lt;/h2&gt;
&lt;p&gt;Initially, I wanted to iterate on a simple command-line method that produces a sequence of characters that can be used as &quot;pixels&quot;. ASCII characters came to help: &lt;code&gt;[&apos; &apos;, &apos;░&apos;, &apos;▒&apos;, &apos;▓&apos;, &apos;█&apos;, &apos;▄&apos;, &apos;▀&apos;]&lt;/code&gt;. Now, with probabilities matrix, the work here is only iterate over a given number of steps and, with a random seed, check the probability of an ocurrency:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function markov(steps = 1000) {
    const states = [&apos; &apos;, &apos;░&apos;, &apos;▒&apos;, &apos;▓&apos;, &apos;█&apos;, &apos;▄&apos;, &apos;▀&apos;];
    const transitionProbabilities = {
        [states[0]]: { [states[0]]: 0.9, [states[1]]: 0.0, [states[2]]: 0.0, [states[3]]: 0.0, [states[4]]: 0.1, [states[5]]: 0.0, [states[6]]: 0.0 },
        [states[1]]: { [states[0]]: 0.1, [states[1]]: 0.9, [states[2]]: 0.0, [states[3]]: 0.0, [states[4]]: 0.9 , [states[5]]: 0.0, [states[6]]: 0.9 },
        [states[2]]: { [states[0]]: 0.0, [states[1]]: 0.1, [states[2]]: 0.9, [states[3]]: 0.0, [states[4]]: 0.0 , [states[5]]: 0.1, [states[6]]: 0.0 },
        [states[3]]: { [states[0]]: 0.0, [states[1]]: 0.0, [states[2]]: 0.1, [states[3]]: 0.9, [states[4]]: 0.1 , [states[5]]: 0.1, [states[6]]: 0.0 },
        [states[4]]: { [states[0]]: 0.0, [states[1]]: 0.0, [states[2]]: 0.0, [states[3]]: 0.0, [states[4]]: 0.0 , [states[5]]: 0.1, [states[6]]: 0.1},
        [states[5]]: { [states[0]]: 0.1, [states[1]]: 0.0, [states[2]]: 0.0, [states[3]]: 0.9, [states[4]]: 0.0 , [states[5]]: 0.1, [states[6]]: 0.0},
        [states[6]]: { [states[0]]: 0.0, [states[1]]: 0.1, [states[2]]: 0.0, [states[3]]: 0.0, [states[4]]: 0.1 , [states[5]]: 0.0, [states[6]]: 0.1},
    }

    let currentState = states[4];
    let accumulator = currentState;

    while (steps &amp;gt; 0) {
        const nextState = states[Math.floor(Math.random() * states.length)];
        const probability = transitionProbabilities[currentState][nextState];
        if (Math.random() &amp;lt; probability) {
            if (steps % 90 === 0) {
                accumulator += &apos;\n&apos;;
            }
            currentState = nextState;
            accumulator += currentState;
            steps--;
        }
    }

    return accumulator;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result? A promising nice pattern like this one:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./naivemarkovimage.webp&quot; alt=&quot;Pattern from Markov chain #pixelated&quot; title=&quot;Output pattern from the Markov chain generator&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Learning (machines)&lt;/h2&gt;
&lt;p&gt;But, the interesting part of Markov chains is their ability to learn from previous sources. In the previous example, the &lt;code&gt;transitionsProbabilities&lt;/code&gt; matrix was deliberated, tunned manually to obtain similar patterns. What does it means? We need to provide a way to build or calculate that matrix from an initial source. A new method must be introduced to perform that calculations. Additionally, it required to evaluate the probabilities from an external image or a set of images.&lt;/p&gt;
&lt;p&gt;As the previous example showed, we have a limited set of &quot;colors&quot;, 7 in total. Reducing the number of colors of the source image will improve the speed of processing and creation of the output image.&lt;/p&gt;
&lt;p&gt;The final matrix will be a simplified representation of the current image, keeping the general structure and probabilities of the original image.&lt;/p&gt;
&lt;p&gt;I wanted an interactive application that can be deployed in any static server: basic HTML, JavaScript and CSS.&lt;/p&gt;
&lt;p&gt;This list of restictions will be a good starting point to develop a more robust solution, but requires going deeper in the following aspects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Image manipulation using the Canvas API.&lt;/li&gt;
&lt;li&gt;Color reduction algorithm.&lt;/li&gt;
&lt;li&gt;Markov chains learning algorithm.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Vibe prompting&lt;/h2&gt;
&lt;p&gt;As something that I wanted to explore quick, I decided to use some generative AI to create this app faster. But as many knows, a good prompt is the key of a good result. Why not use the same tool to create a well documented set of instructions to generate the expected application? Using &lt;a href=&quot;https://gemini.google.com/&quot;&gt;Gemini&lt;/a&gt; 2.5 flash, I asked for:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;write a detailed document that instructs an AI to develop an application in javascript that, using the Markov chains concept, learns from a set of base images, subscaling and reducing their colors to a user defined total. Then, with the obtained probabilities, generates a new image&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The result: an impressive and highly detailed, a thousand words long document that details the requirements of this application: &lt;a href=&quot;https://github.com/manuelhe/markov-image-generator/blob/main/ai-instructions.md&quot;&gt;ai-instructions.md&lt;/a&gt;. It went way beyond my expectations, describing the HTML document, a general requirement for the styles, and the structure of the JavaScript file. But it makes a strong emphasis describing the inner process of the application split in three sections:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Image Preprocessing (Subscaling and Color Reduction)&lt;/li&gt;
&lt;li&gt;Markov Chain Learning&lt;/li&gt;
&lt;li&gt;Image Generation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each step has a brief description and a set of instructions for the AI, e.g.,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Objective&lt;/strong&gt;: Implement the logic to load images, resize them, and reduce their color palette to a user-defined number of colors.
&amp;lt;br&amp;gt;&lt;strong&gt;Instructions for AI&lt;/strong&gt;:
&amp;lt;br&amp;gt;1. Image Loading and Subscaling (&lt;code&gt;processImages&lt;/code&gt; function):
&amp;lt;br&amp;gt;- When the &quot;Process Images &amp;amp; Learn&quot; button is clicked, iterate through the selected files from the file input...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Some instructions offers different alternatives, but makes a final decision at the end,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pixel Traversal&lt;/strong&gt;: For each image, traverse its pixels. Consider different neighborhood relationships for the Markov chain:
&amp;lt;br&amp;gt;- &lt;strong&gt;Option 1 (Simple - 1D)&lt;/strong&gt;: Learn probabilities based on a pixel and its right neighbor (&lt;code&gt;(x,y)&lt;/code&gt; to &lt;code&gt;(x+1,y)&lt;/code&gt;). This is simpler but might produce less cohesive images.
&amp;lt;br&amp;gt;- &lt;strong&gt;Option 2 (Slightly More Complex - 2D)&lt;/strong&gt;: Learn probabilities based on a pixel and its right neighbor AND its bottom neighbor (&lt;code&gt;(x,y)&lt;/code&gt; to &lt;code&gt;(x+1,y)&lt;/code&gt; and &lt;code&gt;(x,y)&lt;/code&gt; to &lt;code&gt;(x,y+1)&lt;/code&gt;). This generally produces better results.
&amp;lt;br&amp;gt;- &lt;strong&gt;AI Choice&lt;/strong&gt;: Implement Option 2 for a more robust model.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;or simply mention other possible solutions,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Recommended Algorithm&lt;/strong&gt;: Use a K-means clustering algorithm or a similar perceptual color reduction method (e.g., Octree Quantization if simpler to implement in JS).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In other sections, it even presents code suggestions:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The structure of markovChainModel should be,&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;color1&quot;: {
        &quot;next_color_1&quot;: probability,
        &quot;next_color_2&quot;: probability,
        // ...
    },
    &quot;color2&quot;: {
        // ...
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or describes an algorith to solve a particular problem,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Counting Transitions&lt;/strong&gt;:
&amp;lt;br&amp;gt;- For each pixel &lt;code&gt;P1&lt;/code&gt; at &lt;code&gt;(x,y)&lt;/code&gt;:
&amp;lt;br&amp;gt; - Get its color &lt;code&gt;C1&lt;/code&gt;.
&amp;lt;br&amp;gt; - Get the color &lt;code&gt;C2&lt;/code&gt; of its right neighbor &lt;code&gt;P2&lt;/code&gt; at &lt;code&gt;(x+1,y)&lt;/code&gt; (if within bounds).
&amp;lt;br&amp;gt; - Get the color &lt;code&gt;C3&lt;/code&gt; of its bottom neighbor &lt;code&gt;P3&lt;/code&gt; at &lt;code&gt;(x,y+1)&lt;/code&gt; (if within bounds).
&amp;lt;br&amp;gt; - Increment counts in &lt;code&gt;markovChainModel&lt;/code&gt;:
&amp;lt;br&amp;gt; - &lt;code&gt;markovChainModel[C1][C2]&lt;/code&gt; (for horizontal transitions)
&amp;lt;br&amp;gt; - &lt;code&gt;markovChainModel[C1][C3]&lt;/code&gt; (for vertical transitions)
&amp;lt;br&amp;gt;- Handle edge cases (last row/column pixels).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On a following step, called &lt;em&gt;Refinements and Error Handling&lt;/em&gt;, it makes some suggestions related with the user interface, like &lt;em&gt;Input Validation&lt;/em&gt; or &lt;em&gt;User Feedback&lt;/em&gt;. Others related with the &lt;em&gt;Performance Considerations&lt;/em&gt; and &lt;em&gt;Visual Quality Enhancements&lt;/em&gt;. It even dedicate a section to the &lt;em&gt;Code Structure&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Finally, a list of deliverables where, alongside the item, it presents a really brief description of each file. Another deliverable suggested is a documentation (it doesn´t specify an output format), highlighting the use instructions, the technical details behind the Markov chain implementation, and possible limitations of the app.&lt;/p&gt;
&lt;h2&gt;Vibe coding&lt;/h2&gt;
&lt;p&gt;Using the &lt;strong&gt;Canvas&lt;/strong&gt; option enabled in the Gemini web interface, I asked to build the solution:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Using the previous response, generate the described application&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Well, after a short wait, I got this,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I have created the Markov Chain Image Generation application you described. Below are the complete, self-contained files for the application. You can save these three files (&lt;code&gt;index.html&lt;/code&gt;, &lt;code&gt;style.css&lt;/code&gt;, and &lt;code&gt;script.js&lt;/code&gt;) in the same folder and open &lt;code&gt;index.html&lt;/code&gt; in your web browser to run the application.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;followed by three files ready to be downloaded. The &lt;strong&gt;Canvas&lt;/strong&gt; option in the Gemini UI, allows the live previsualization of web applications.&lt;/p&gt;
&lt;h3&gt;Mixed results&lt;/h3&gt;
&lt;p&gt;The first iteration returned an almost unusable application, with missing links to the correct Tailwind styles and broken JavaScript methods. after aking to fix it, it came with a decent result that,at the end required manual tweaking in most of its parts.&lt;/p&gt;
&lt;p&gt;Here you can experiment with the manually edited result: &lt;a href=&quot;https://morra.co/markov-image-generator/&quot;&gt;Markov Image Generator &lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;This is a promising technology that will transform in less than a year the way software is developed. Engineers will need a new set of skills required to orchestrate autonomous workers, understanding problems in a macro perspective, know how to write tailored instructions, and be aware of the limitations of the current tools.&lt;/p&gt;
&lt;p&gt;Great times will come, scary but thrilling times!&lt;/p&gt;
</content:encoded><author>Manuel Herrera</author></item><item><title>Shrink Your Stash: Breaking Down the Ultimate WebP Converter Script</title><link>https://f20x.com/posts/bulkwebp-bash-script</link><guid isPermaLink="true">https://f20x.com/posts/bulkwebp-bash-script</guid><description>Hoarding massive images? Let´s dissect a slick Bash script that multi-threads WebP conversions.</description><pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hey there. So, you’ve been hoarding high-res PNGs and JPEGs. Maybe it’s a folder of 4K cat photos, maybe it’s an overblown website assets directory. We’ve all been there. Well, as an Web Developer, I have a physical hard drive that gets bogged down with giant files, and absolutely feel your pain when things run slower than they need to.&lt;/p&gt;
&lt;p&gt;Today, we’re going to take a nice, slow walk through a pretty slick Bash script called &lt;code&gt;bulkwebp&lt;/code&gt; (&lt;a href=&quot;https://gist.github.com/manuelhe/b63057edce8f8f79523a0baf1c399dd7&quot;&gt;source code&lt;/a&gt;). It doesn’t just convert your images to WebP (Google&apos;s highly efficient image format); it throws all your CPU cores at the problem so you aren&apos;t waiting around until the next ice age, and then it gives you a neat little receipt of the megabytes you saved.&lt;/p&gt;
&lt;p&gt;Let’s kick back, grab a beverage, and dissect how this bad boy works, piece by piece.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Setting the Stage (and Finding Your Cores)&lt;/h2&gt;
&lt;p&gt;Right out of the gate, the script establishes some ground rules.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;REPLACE=false
RECURSIVE_MODE=false 
CORES=$(nproc 2&amp;gt;/dev/null || sysctl -n hw.ncpu 2&amp;gt;/dev/null || echo 1) 
FILE_TYPE=&quot;&quot; 
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Safety Nets:&lt;/strong&gt; &lt;code&gt;REPLACE&lt;/code&gt; and &lt;code&gt;RECURSIVE_MODE&lt;/code&gt; default to &lt;code&gt;false&lt;/code&gt;. The script isn&apos;t going to go rogue and delete your original files or dive deep into your forbidden subdirectories unless you explicitly tell it to.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Core Sniffer:&lt;/strong&gt; That &lt;code&gt;CORES&lt;/code&gt; line is a thing of beauty. It tries to use &lt;code&gt;nproc&lt;/code&gt; (common on Linux) to see how many CPU cores you have. If that fails, it tries &lt;code&gt;sysctl&lt;/code&gt; (the macOS way). If &lt;em&gt;that&lt;/em&gt; fails, it just throws its hands up and assumes you have &lt;code&gt;1&lt;/code&gt; core. It&apos;s doing the heavy lifting so you don&apos;t have to guess your machine&apos;s specs.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;2. The Bouncer: Argument Parsing&lt;/h2&gt;
&lt;p&gt;Next up, the script has to figure out what you actually want it to do based on the flags you passed in the terminal.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;while [ $# -gt 0 ]; do
  case &quot;$1&quot; in
    -r|--recursive)
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This &lt;code&gt;while&lt;/code&gt; loop is basically a bouncer at the club, checking IDs. It looks at the first argument (&lt;code&gt;$1&lt;/code&gt;), figures out what flag it is (like &lt;code&gt;-r&lt;/code&gt; or &lt;code&gt;--cores&lt;/code&gt;), sets the internal variable, and then uses &lt;code&gt;shift&lt;/code&gt; to kick that argument out of line so it can check the next one.&lt;/p&gt;
&lt;h2&gt;:::note
Look at the &lt;code&gt;-t|--type&lt;/code&gt; block. It actually strips out a leading dot if you accidentally type &lt;code&gt;-t .png&lt;/code&gt; instead of &lt;code&gt;-t png&lt;/code&gt;. That is some excellent, forgiving UX design right there in a terminal script!
:::&lt;/h2&gt;
&lt;h2&gt;3. The Bloodhound: Building the &lt;code&gt;find&lt;/code&gt; Command&lt;/h2&gt;
&lt;p&gt;Instead of hardcoding how to search for files, the script dynamically builds a &lt;code&gt;find&lt;/code&gt; command based on the flags you set.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Staying shallow:&lt;/strong&gt; If &lt;code&gt;RECURSIVE_MODE&lt;/code&gt; is false, it slaps &lt;code&gt;-maxdepth 1&lt;/code&gt; onto the command so it only looks in the current folder.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Picking targets:&lt;/strong&gt; If you specified a file type, it targets that. Otherwise, it casts a wide net for the usual suspects: &lt;code&gt;.png&lt;/code&gt;, &lt;code&gt;.jpg&lt;/code&gt;, &lt;code&gt;.jpeg&lt;/code&gt;, and &lt;code&gt;.tiff&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By the end of this section, &lt;code&gt;FIND_CMD&lt;/code&gt; is a fully loaded string ready to sniff out your hefty images.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. The Heavy Lifting: &lt;code&gt;xargs&lt;/code&gt; and Parallel Processing&lt;/h2&gt;
&lt;p&gt;This is where the script goes from &quot;neat&quot; to &quot;absolute beast.&quot;&lt;/p&gt;
&lt;p&gt;Instead of converting files one by one in a slow, agonizing queue, it uses &lt;code&gt;xargs -P &quot;$CORES&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Exporting Variables:&lt;/strong&gt; It uses &lt;code&gt;export REPLACE&lt;/code&gt; so the mini-scripts running in parallel know if they have permission to delete the original files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Payload (&lt;code&gt;SCRIPT&lt;/code&gt;):&lt;/strong&gt; It defines a chunk of code that uses &lt;code&gt;cwebp&lt;/code&gt; to do the actual conversion. It also uses &lt;code&gt;wc -c&lt;/code&gt; to count the exact byte size of the original file and the new WebP file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Secret Messages:&lt;/strong&gt; Notice how it &lt;code&gt;echo&lt;/code&gt;s out things like &lt;code&gt;✅ Converted&lt;/code&gt; but &lt;em&gt;also&lt;/em&gt; outputs a weird line like &lt;code&gt;SIZE_STATS 150000 45000&lt;/code&gt;? That is a hidden data string meant for the next part of our script. It’s like the workers passing notes to the accountant.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;5. The Accountant: &lt;code&gt;awk&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Finally, all this parallel terminal output gets piped (&lt;code&gt;|&lt;/code&gt;) into &lt;code&gt;awk&lt;/code&gt;, which is basically the spreadsheet nerd of the Linux world.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;awk &apos;/^SIZE_STATS/ {
    orig_total += $2
    new_total += $3
    next
}
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;awk&lt;/code&gt; sits at the end of the pipeline, reading every line of text that the conversion workers spit out.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If it sees a line starting with &lt;code&gt;SIZE_STATS&lt;/code&gt;, it says, &lt;em&gt;&quot;Ah, data!&quot;&lt;/em&gt; It intercepts those numbers, adds them to a running total, and hides the line from your screen (&lt;code&gt;next&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;If it sees any other line (like the green checkmarks or the trash can emojis), it just prints it normally so you can watch the progress.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once all the files are done (the &lt;code&gt;END&lt;/code&gt; block), &lt;code&gt;awk&lt;/code&gt; does some quick math to convert those bytes into Megabytes, calculates the percentage of space you saved, and prints out a beautiful little receipt.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Verdict&lt;/h2&gt;
&lt;p&gt;This script is a fantastic example of gluing together standard Unix tools (&lt;code&gt;find&lt;/code&gt;, &lt;code&gt;xargs&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt;) to create something highly efficient. It respects your time by using parallel processing, and it respects your terminal by giving you a clean, readable summary at the end instead of just vomiting data onto the screen.&lt;/p&gt;
&lt;h2&gt;The Full Script&lt;/h2&gt;
&lt;p&gt;In case you where wondering what I&apos;m talking about, and you missed the link in the introduction, here you have the full script for your delight:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash

# Default values for flags
REPLACE=false
RECURSIVE_MODE=false # Use a simple boolean for logic
CORES=$(nproc 2&amp;gt;/dev/null || sysctl -n hw.ncpu 2&amp;gt;/dev/null || echo 1) # Get core count
FILE_TYPE=&quot;&quot; # Variable to store the optional file extension

##
# Displays help message
##
show_help() {
cat &amp;lt;&amp;lt; EOF
Usage: bulkwebp [OPTIONS]
Converts images in the directory to WebP format and calculates space saved.

This high-performance version processes files in parallel for maximum speed.

Options:
  -r, --recursive   Recursively search for images in all subdirectories.
  -p, --replace     Delete the original source image after a successful conversion.
  -c, --cores NUM   Specify the number of parallel jobs (default: all available cores).
  -t, --type EXT    Specify a single input file extension to convert (e.g., png, jpg). Defaults to all common formats.
  -h, --help        Display this help message and exit.
EOF
}

# --- Argument Parsing ---
while [ $# -gt 0 ]; do
  case &quot;$1&quot; in
    -r|--recursive)
      RECURSIVE_MODE=true
      shift
      ;;
    -p|--replace)
      REPLACE=true
      shift
      ;;
    -c|--cores)
      CORES=&quot;$2&quot;
      shift 2
      ;;
    -t|--type)
      # Remove any leading dot if the user accidentally typed &quot;-t .png&quot; instead of &quot;-t png&quot;
      FILE_TYPE=&quot;${2#.}&quot; 
      shift 2
      ;;
    -h|--help)
      show_help
      exit 0
      ;;
    *)
      echo &quot;Error: Unknown option &apos;$1&apos;&quot; &amp;gt;&amp;amp;2
      show_help
      exit 1
      ;;
  esac
done

# --- Build find command ---
# Start with the base command
FIND_CMD=&quot;find .&quot;

# Add maxdepth option ONLY if not in recursive mode
if [ &quot;$RECURSIVE_MODE&quot; = &quot;false&quot; ]; then
    FIND_CMD+=&quot; -maxdepth 1&quot;
fi

# Add the file type and name patterns dynamically
if [ -n &quot;$FILE_TYPE&quot; ]; then
    # If a specific type is provided, only look for that extension
    FIND_CMD+=&quot; -type f -iname &apos;*.$FILE_TYPE&apos; -print0&quot;
else
    # Default behavior: look for standard image formats
    FIND_CMD+=&quot; -type f \( -iname &apos;*.png&apos; -o -iname &apos;*.jpg&apos; -o -iname &apos;*.jpeg&apos; -o -iname &apos;*.tiff&apos; \) -print0&quot;
fi

# --- Main Execution ---
# Export the REPLACE variable so it&apos;s available to the subshells created by xargs
export REPLACE

# The `sh -c &apos;...&apos;` script that will be executed by xargs for each file
SCRIPT=&apos;
    filepath=&quot;$1&quot;
    outfile=&quot;${filepath%.*}.webp&quot;
    
    # Extract original size in bytes (cross-platform friendly)
    orig_size=$(wc -c &amp;lt; &quot;$filepath&quot;)
    
    if cwebp -quiet &quot;$filepath&quot; -o &quot;$outfile&quot;; then
        # Extract new size in bytes
        new_size=$(wc -c &amp;lt; &quot;$outfile&quot;)
        
        echo &quot;✅ Converted: $outfile&quot;
        # Output a hidden data string for awk to intercept and calculate
        echo &quot;SIZE_STATS $orig_size $new_size&quot;
        
        if [ &quot;$REPLACE&quot; = &quot;true&quot; ]; then
            rm &quot;$filepath&quot;
            echo &quot;🗑️  Removed:   $filepath&quot;
        fi
    else
        echo &quot;❌ Failed:    $filepath&quot; &amp;gt;&amp;amp;2
    fi
&apos;

# Execute the command using a pipe and xargs for parallel processing.
eval &quot;$FIND_CMD&quot; | xargs -0 -P &quot;$CORES&quot; -I {} sh -c &quot;$SCRIPT&quot; _ {} | awk &apos;
/^SIZE_STATS/ {
    orig_total += $2
    new_total += $3
    next # Skip printing this specific line
}
{ print } # Print all other lines (like ✅ Converted or 🗑️ Removed)
END {
    if (orig_total &amp;gt; 0) {
        saved = orig_total - new_total
        percent = (saved / orig_total) * 100
        
        printf &quot;\n--------------------------------------\n&quot;
        printf &quot;📊 Total Original Size: %.2f MB\n&quot;, orig_total / 1048576
        printf &quot;📊 Total WebP Size:     %.2f MB\n&quot;, new_total / 1048576
        if (saved &amp;gt; 0) {
            printf &quot;🎉 Total Space Saved:   %.2f MB (%.1f%% reduction)\n&quot;, saved / 1048576, percent
        } else {
            printf &quot;⚠️ Space Increased by:   %.2f MB\n&quot;, (new_total - orig_total) / 1048576
        }
        printf &quot;--------------------------------------\n&quot;
    } else {
        print &quot;\nNo images were found or converted.&quot;
    }
}&apos;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded><author>Manuel Herrera</author></item><item><title>From Blueprint to Caribbean Breezes: How We Built a Multilingual Web Platform for 1/10th the Cost Using Gemini</title><link>https://f20x.com/posts/building-rental-property-site</link><guid isPermaLink="true">https://f20x.com/posts/building-rental-property-site</guid><description>An analysis of how OceanViewFlats was built with Gemini for a fraction of the cost, highlighting why expert developer guidance remains vital to AI-driven engineering.</description><pubDate>Mon, 25 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;From Blueprint to Caribbean Breezes: How We Built a Multilingual Web Platform for 1/10th the Cost Using Gemini&lt;/h1&gt;
&lt;p&gt;We are witnessing a fundamental shift in how software is created. This isn&apos;t just about automated code completion; it is about a new era of software craftsmanship where an experienced developer and an advanced Large Language Model (LLM)—specifically Google&apos;s Gemini—work in tight coordination to build, optimize, and deploy complete production systems.&lt;/p&gt;
&lt;p&gt;To illustrate this shift, let&apos;s look behind the scenes at &lt;a href=&quot;https://www.oceanviewflats.com/&quot;&gt;&lt;strong&gt;OceanViewFlats&lt;/strong&gt;&lt;/a&gt;, a high-end, multilingual direct-booking web platform built for two premium vacation rentals in Santa Marta, Colombia. This project was developed almost entirely through an AI-human pair programming pipeline.&lt;/p&gt;
&lt;p&gt;Below is an analysis of the problems this platform solves, the indispensable role of the expert developer guiding the AI, and a realistic cost comparison between traditional development and this new AI-first workflow.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;🏖️ 1. The Key Problem: Direct Bookings vs. Platform Fees&lt;/h2&gt;
&lt;p&gt;In the vacation rental industry, property owners face a persistent dilemma. Platforms like Airbnb and Booking.com provide incredible global reach but charge steep commission rates—often between &lt;strong&gt;12% to 20%&lt;/strong&gt; split between the host and the guest. For a premium stay, these platform fees can add hundreds of dollars to a single booking, driving up costs for travelers and eroding margins for owners.&lt;/p&gt;
&lt;p&gt;To bypass these fees, properties need a &lt;strong&gt;direct booking channel&lt;/strong&gt;. However, a successful direct booking website cannot just be a simple, generic template. It must solve several complex challenges simultaneously:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multilingual Friction&lt;/strong&gt;: Premium properties attract an international audience. OceanViewFlats needs to serve guests in &lt;strong&gt;6 languages&lt;/strong&gt; (English, Spanish, French, Italian, German, and Japanese) seamlessly, with automatic language detection and flawless localization.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extreme Performance &amp;amp; Mobile UX&lt;/strong&gt;: Travelers often book on the move. The site must load instantly, use compressed modern image formats (like WebP) to prevent heavy bandwidth consumption on mobile networks, and remain fully interactive under poor cellular coverage.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Traditional &amp;amp; Generative Search Visibility (SEO/GEO)&lt;/strong&gt;: The site must rank on traditional search engines (Google, Bing) and, crucially, be easily extracted by generative answer engines (ChatGPT, Perplexity, Claude, and Google SGE) when users ask queries like, &lt;em&gt;&quot;Show me beachfront apartments in Santa Marta with high-speed Wi-Fi and direct bookings.&quot;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;OceanViewFlats&lt;/strong&gt; solves these problems with a statically compiled, high-performance architecture built in React, TypeScript, and Vite. It generates 24 independent, localized page variations, serves highly optimized WebP thumbnails, and embeds deep, multi-entity structured schemas.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;🧠 2. The Vital Need for Expert Developer Guidance&lt;/h2&gt;
&lt;p&gt;There is a common misconception that LLMs can build complex software autonomously from a simple one-sentence prompt. In reality, letting an AI build code without expert oversight almost always results in a fragile &quot;Minimum Viable Product&quot; full of technical debt, accessibility gaps, and unoptimized assets.&lt;/p&gt;
&lt;p&gt;Getting premium, production-ready results from Gemini requires &lt;strong&gt;expert developer instructions&lt;/strong&gt;. In this project, the developer acted as the &lt;strong&gt;architect, auditor, and validator&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Architecting the Static Generation Pipeline&lt;/strong&gt;:
Instead of choosing a heavy, expensive server-side framework, the developer directed the AI to build a custom static pre-rendering compiler using React, TypeScript, and a simple Node script (&lt;code&gt;render.tsx&lt;/code&gt;). This kept the hosting costs at absolute zero while ensuring maximum SEO indexability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enforcing Premium Engineering Principles&lt;/strong&gt;:
When implementing the mobile-responsive features, the developer didn&apos;t accept basic &quot;hide-on-mobile&quot; shortcuts. They instructed the AI to design a persistent, sticky, horizontally swipeable specification bar that stacks perfectly below the navigation bar, complete with custom CSS utility classes like &lt;code&gt;scrollbar-none&lt;/code&gt; to prevent visual clutter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimizing Asset Pipelines&lt;/strong&gt;:
An AI will happily reference massive 5MB source images in a gallery. The developer directed a batch compilation utilizing &lt;code&gt;imagemagick&lt;/code&gt; to generate compressed WebP thumbnails and set up native lazy-loading and fetch priorities (&lt;code&gt;fetchPriority=&quot;low&quot;&lt;/code&gt; and &lt;code&gt;fetchPriority=&quot;high&quot;&lt;/code&gt;) to slash first-load page weight by &lt;strong&gt;over 92%&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implementing Advanced GEO (Generative Engine Optimization)&lt;/strong&gt;:
A novice might ask for standard search tags. The expert developer instructed Gemini to write a deep, multi-entity JSON-LD schema using schema graphs (&lt;code&gt;@graph&lt;/code&gt;). This mapped the brand entity (&lt;code&gt;LodgingBusiness&lt;/code&gt;) on the Home page and exposed specific, high-extraction attributes (bedrooms, bathrooms, and nightly &lt;code&gt;AggregateOffer&lt;/code&gt; pricing structures) on the property pages—precisely the signals that AI search crawlers use to extract answers for user queries.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Without the expert developer&apos;s guidance, the AI would not know &lt;em&gt;what&lt;/em&gt; to optimize or &lt;em&gt;how&lt;/em&gt; to build it cleanly. The developer provides the standards, structural vision, and constraints; the AI provides the raw, high-speed execution.&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;💵 3. Cost Analysis: Traditional vs. AI-Disruptive Workflows&lt;/h2&gt;
&lt;p&gt;To understand the economic disruption of this workflow, let&apos;s compare the real-world costs of building the OceanViewFlats platform using traditional agency development versus the AI-assisted pipeline.&lt;/p&gt;
&lt;h3&gt;The Scope of Work&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A custom, responsive, statically generated multilingual website (6 languages: EN, ES, FR, IT, DE, JA).&lt;/li&gt;
&lt;li&gt;4 core pages (Home, Flat 1707, Flat 1606, Contact) generating 24 static HTML variants.&lt;/li&gt;
&lt;li&gt;Deep SEO/GEO optimization: bidirectional &lt;code&gt;hreflang&lt;/code&gt; maps, dynamic Open Graph localized alternates, dynamic sitemap compiler, and robust multi-entity JSON-LD schemas.&lt;/li&gt;
&lt;li&gt;Interactive, responsive lightbox gallery with optimized asset pipelines (WebP compression).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;Traditional Development Approach&lt;/h3&gt;
&lt;p&gt;In a traditional agency setting, this project would require a Senior Front-End/Full-Stack Engineer, a QA Analyst, and a Project Manager/Designer.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase / Role&lt;/th&gt;
&lt;th&gt;Estimated Human Hours&lt;/th&gt;
&lt;th&gt;Hourly Rate (USD)&lt;/th&gt;
&lt;th&gt;Total Cost (USD)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UX/UI Design &amp;amp; Copywriting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;12 hours&lt;/td&gt;
&lt;td&gt;$75 / hr&lt;/td&gt;
&lt;td&gt;$900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Front-End Development &amp;amp; Compiler Setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;45 hours&lt;/td&gt;
&lt;td&gt;$100 / hr&lt;/td&gt;
&lt;td&gt;$4,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multilingual Copy &amp;amp; Translation Setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10 hours&lt;/td&gt;
&lt;td&gt;$75 / hr&lt;/td&gt;
&lt;td&gt;$750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SEO/GEO Schema Engineering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8 hours&lt;/td&gt;
&lt;td&gt;$100 / hr&lt;/td&gt;
&lt;td&gt;$800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QA, Mobile Testing &amp;amp; Deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10 hours&lt;/td&gt;
&lt;td&gt;$75 / hr&lt;/td&gt;
&lt;td&gt;$750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Project Management / Overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8 hours&lt;/td&gt;
&lt;td&gt;$80 / hr&lt;/td&gt;
&lt;td&gt;$640&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TOTALS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;93 Hours&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;—&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$8,340 USD&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Time-to-market: 3 to 5 weeks.&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;AI-Assisted Disruptive Workflows (Using Gemini)&lt;/h3&gt;
&lt;p&gt;In this model, a single &lt;strong&gt;Senior Software Engineer&lt;/strong&gt; directs Gemini (using tools like Antigravity) to write the code, compile pages, compress assets, and structure schemas.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase / Resource&lt;/th&gt;
&lt;th&gt;Actual Human Hours&lt;/th&gt;
&lt;th&gt;Hourly Rate / Tool Cost (USD)&lt;/th&gt;
&lt;th&gt;Total Cost (USD)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gemini Pro / Flash API &amp;amp; Tooling Fees&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Token usage &amp;amp; licensing&lt;/td&gt;
&lt;td&gt;$15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Expert Engineer Prompting &amp;amp; Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3.5 hours&lt;/td&gt;
&lt;td&gt;$100 / hr&lt;/td&gt;
&lt;td&gt;$350&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Expert Auditing, Schema Validation &amp;amp; QA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.5 hours&lt;/td&gt;
&lt;td&gt;$100 / hr&lt;/td&gt;
&lt;td&gt;$150&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment &amp;amp; Verification&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.0 hours&lt;/td&gt;
&lt;td&gt;$100 / hr&lt;/td&gt;
&lt;td&gt;$100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TOTALS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6.0 Hours&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;—&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$615 USD&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Time-to-market: 1 to 2 days.&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Financial Comparison Summary&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Traditional Workflow&lt;/th&gt;
&lt;th&gt;AI-Assisted Workflow&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Financial Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$8,340 USD&lt;/td&gt;
&lt;td&gt;$615 USD&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;92.6% Cost Reduction&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total Human Hours&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;93 Hours&lt;/td&gt;
&lt;td&gt;6 Hours&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;93.5% Time Savings&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delivery Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;21 - 35 Days&lt;/td&gt;
&lt;td&gt;1 - 2 Days&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Over 15x Faster&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;🏁 4. Conclusion: The Future is Symbiotic&lt;/h2&gt;
&lt;p&gt;The OceanViewFlats project demonstrates that AI-driven development is no longer a future promise—it is a current, highly disruptive reality. Achieving a &lt;strong&gt;92% reduction in costs&lt;/strong&gt; and accelerating delivery from weeks to hours changes the unit economics of web development. It makes bespoke, premium, highly optimized software accessible to small businesses and property owners who previously had to settle for generic, unoptimized site-builders.&lt;/p&gt;
&lt;p&gt;However, the most significant takeaway from this exercise is that &lt;strong&gt;AI does not replace human expertise; it acts as a massive force multiplier for it&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;An AI model has access to vast amounts of coding syntax, but it lacks real-world contextual awareness, business intuition, aesthetic taste, and strict engineering standards. It doesn&apos;t know that a sticky navigation bar might overlap a property details bar unless directed to check. It doesn&apos;t know how to structure schema graphs to establish local business authority for AI agents unless an expert understands those indexing nuances.&lt;/p&gt;
&lt;p&gt;This new way of working is here to stay. The developers who thrive in this next decade will not be those who write code the fastest, but those who are &lt;strong&gt;expert instructors&lt;/strong&gt;—engineers who can conceptualize elegant architectures, direct AI models with high precision, audit output with a critical eye, and translate complex human business requirements into flawlessly executed digital experiences.&lt;/p&gt;
</content:encoded><author>Manuel Herrera</author></item></channel></rss>