Category: Engineering

  • Engineering Expression

    Engineering Expression

    Engineering Experiences That Feel Alive

    I’ve never seen frontend as just “making the design work.” It’s not about lining up pixels or matching a Figma spec.

    To me, frontend has always been an act of translation. Taking an idea and giving it movement, rhythm, and emotion.

    I’m an Experience Engineer, which really means I care as much about how something feels as how it functions.


    Between Art and Engineering

    Most interfaces get the job done. They’re usable, fast enough, accessible enough. But only a few manage to stay with you after you’ve closed the tab.

    Somewhere between art and engineering is a small space where code can make you feel something —
    where a layout breathes, a transition speaks, and the screen feels just a little bit more alive.

    That’s the space I like to work in.

    Making that happen isn’t about flashy animations or decoration.
    It’s about intent — giving meaning to interaction.
    Every delay, easing curve, and hover response is a chance to say something subtle.


    What “Experience” Really Means

    We throw that word around a lot — experience.
    But it’s real, and it’s measurable.

    It’s the emotional response to performance, timing, and feedback.
    It’s how the page moves when you touch it.
    How a button reacts before it’s clicked.
    It’s those micro-decisions that build trust and personality.

    That’s what I’m chasing — the small, invisible details that make a product feel cohesive and alive.


    How I Work

    My process is part system, part intuition.

    I rely on design tokens and theme.json to keep everything consistent and responsive.
    I use Framer Motion or GSAP to give motion purpose — not noise.
    AI tools help me test and document faster, but they’re just that: tools.

    The real work is making sure every component carries the same emotional language.
    Because when structure, motion, and performance align, you get something that feels effortless — and that’s what experience really is.

    It’s code with a pulse.


    The Experience Stack

    When I think about building experiences, I think in layers:

    1. Structure: semantic, accessible HTML that makes sense
    2. Emotion: motion, timing, and interaction feedback
    3. Performance: quick, reliable, smooth
    4. Continuity: tokens, iteration, and maintainable systems

    Together, these form what I call the experience stack
    a way of thinking that bridges technical performance and emotional resonance.


    Where Frontend Is Headed

    AI will keep changing how we write code.
    But empathy, timing, and feel — those aren’t going anywhere.

    The next wave of frontend isn’t about syntax or frameworks.
    It’s about translating logic into emotion.
    About making the invisible, felt.

    That’s where I want to stay —
    somewhere between the art and the codebase,
    building experiences that feel alive.

  • From Full-Stack to Full-Cycle

    From Full-Stack to Full-Cycle

    The Rise of the AI-Supported Generalist.

    The role of a developer has always been expanding. Once, being full-stack meant juggling frontend and backend. Later, it meant cloud deployment, APIs, and maybe even DevOps pipelines. But if we zoom out, building a product involves far more than code. It spans client outreach, design, testing, documentation, analytics, and continuous strategy.

    That’s why I believe the next evolution is full-cycle. It’s about owning the entire arc of product creation, from the first client conversation to data-driven iteration after launch. These things can be supported at every step by AI and modern tooling.


    The Full-Cycle Stack

    Client Acquisition & Outreach

    The product journey starts before code is written. Traditionally, this step required a sales or marketing team. Today, AI makes client acquisition more accessible.

    • How AI Helps: Drafting proposals, creating pitch decks, personalizing outreach emails, and even analyzing leads.
    • Notable Tools: ChatGPT or Claude for proposal drafts, Jasper or Copy.ai for polished marketing copy, Notion AI to track CRM notes, and Apollo.io for lead generation.

    Research & Discovery

    Before building, you need to deeply understand the client’s needs, the market, and the tech landscape.

    • How AI Helps: NotebookLM and Perplexity transform raw documents, whitepapers, or competitor sites into a living, queryable knowledge base. Instead of siloed notes, you get an interactive research layer.
    • Tools: NotebookLM, Obsidian, Perplexity (domain research with citations).

    Design & UX

    A good product isn’t just functionally correct, it’s intuitive and accessible. AI now accelerates prototyping and validation.

    • How AI Helps: Wireframing from sketches, generating UI variations, and running accessibility checks early. Designers can iterate faster without losing creative control.
    • Tools: Figma + Magician AI or Genius (AI-assisted design plugins), Uizard (turn sketches into prototypes), Stark (accessibility audits), Midjourney can even help gather mood boards and ideas.

    Backend & Frontend Development

    The core of full-stack development is still here but AI makes it easier to move across languages and frameworks.

    • How AI Helps: Cursor and GitHub Copilot function like context-aware pair programmers. They scaffold, explain, and optimize code, reducing time spent debugging syntax trivia.
    • Tools: Cursor (AI-first IDE), GitHub Copilot (inline coding assistant), Replit Ghostwriter (rapid prototyping), LangChain / Vercel AI SDK (building AI features into apps).

    DevOps & Deployment

    Getting a product live used to require an ops specialist, or some very carefully built pipelines. With AI, developers can manage infra confidently.

    • How AI Helps: Generate Dockerfiles, CI/CD pipelines, and infrastructure-as-code templates while explaining trade-offs. AI copilots also surface deployment issues quickly.
    • Tools: GitHub Actions + Copilot (pipeline scaffolding), Terraform + Pulumi AI (infra-as-code with AI support), Railway / Render (AI-friendly deployment platforms).

    QA & Testing

    Testing is often under-prioritized, but AI removes friction here.

    • How AI Helps: Generate unit and integration tests automatically, simulate edge cases, and provide regression coverage. QA becomes proactive instead of reactive.
    • Tools: CodiumAI / TestGPT (test generation), Cypress tests can be assisted with Cursor/Github Copilot, SonarLint (code quality + static analysis).

    Documentation (Automated & Continuous)

    Documentation is the lifeblood of maintainable projects and one of the easiest to let slide.

    • How AI Helps: Generate developer docs, API references, onboarding guides, and changelogs directly from code and commits. Instead of being a separate chore, documentation evolves alongside the codebase.
    • Tools: Mintlify (AI-generated docs from code), Swimm (interactive walkthroughs), Docusaurus + GPT pipelines (custom doc sites), ReadMe AI (dynamic API docs).

    Maintenance & Support

    After launch, the product still requires care.

    • How AI Helps: Triage bug reports, summarize issue threads, draft support responses, and provide self-updating FAQs. The support burden is reduced without losing the human touch.
    • Tools: Intercom Fin AI (automated customer responses), Zendesk + AI Assist (support automation), Linear AI summaries (for bug/issue triage).

    Continuous Strategy

    A product isn’t finished at launch, it’s evolving. I’ve always believed a site is an iterative process and should constantly be improved.

    Strategy needs to adapt in real time.

    • How AI Helps: Model scenarios, forecast usage, and create roadmaps informed by real-time data and simulations. Instead of quarterly updates, strategy becomes continuous.
    • Tools: Causal (financial + strategic modeling), Amplitude + AI insights (product strategy analytics), Miro AI (collaborative roadmapping).

    Data Analysis & Iteration

    Iteration closes the cycle. Data-driven decisions are how products grow, but traditionally this meant waiting on a BI team.

    • How AI Helps: Analyze product metrics directly, run exploratory data analysis, and simulate A/B test outcomes. AI enables developers to act as data analyst generalists.
    • Tools: ChatGPT Code Interpreter (EDA + visualization), Hex (data notebooks), Mixpanel + AI insights (usage funnels), Google Analytics Intelligence (natural-language queries).

    The AI-Supported Generalist

    The full-cycle engineer isn’t doing everything alone, they’re leveraging AI to expand their reach. Specialists will always matter, but the AI-supported generalist can bridge gaps, maintain momentum, and keep projects moving forward across the entire lifecycle.

    This role is less about replacing people and more about collapsing silos. AI gives us the leverage to handle research, strategy, design, code, QA, documentation, and data as one continuous loop.


    Why Full-Cycle Matters

    “Full-stack” defined an era where developers expanded vertically across code layers.

    “Full-cycle” defines the era where we expand horizontally across the product lifecycle.

    From first outreach to post-launch iteration, the cycle never stops. And with AI, the engineer who can flex across it isn’t overwhelmed, they’re supported, accelerated, and amplified.

    The future isn’t just full-stack.
    The future is full-cycle.

  • Building Spindle, an Open Source Web-Based Layout Builder

    Building Spindle, an Open Source Web-Based Layout Builder

    In early June I stumbled across Layouts.dev, a visual layout builder that offered a fast way to scaffold UIs with drag-and-drop ease. The concept was great—but I wanted something more open, developer-first, and extensible. Something that could be used like a real tool, not just a toy.

    That moment kicked off a journey to create Spindle—a blazing-fast, open-source layout builder designed for engineers to prototype components and templates with minimal friction. This blog post walks through the inspiration, research, naming conventions, and technical planning that shaped Spindle into a real, usable tool.


    🔍 The Inspiration: Layouts.dev and the Need for Speed

    Layouts.dev is visually appealing, intuitive, and useful for quickly generating UI mockups. I wanted my own version of this but considering:

    • What if it supported a minimal syntax like Pug, but was custom-built for layout thinking?
    • What if it was portable, extensible, and capable of outputting code I could plug into a WordPress theme—or anywhere else?

    Thus began the exploration.


    🧠 The Research & Design Phase

    Over several collaborative brainstorms (many powered by ChatGPT), we mapped out:

    • Existing tools and their limitations
    • Developer pain points in prototyping UI
    • The desire for minimal syntax, instant feedback, and no build steps

    We focused on key features:

    • A custom DSL, like Pug, that engineers could write quickly
    • A web-based editor with live preview
    • Component support and layout composition
    • Export options for WordPress, JSX, or plain HTML

    We also discussed potential integration with CMSs like WordPress, while keeping the tool standalone and framework-agnostic.


    🧵 The Naming System: Weaving an Identity

    The name Spindle emerged early as the project codename. It metaphorically evokes spinning layouts together, creating structure, and speed. From there, a naming system spun into place:

    • Spindle — The platform itself
    • Loom Editor — The in-browser editor where you write layout code
    • Threads DSL — Our minimal, Pug-inspired syntax for layouts
    • Fiber Library — Reusable component snippets
    • Warp Preview — The live output pane for real-time feedback
    • Bobbin Projects — Project save/load system (planned)

    The names are thematic but minimal—memorable without confusing new users.


    ⚙️ Planning the MVP

    I scoped out an MVP that could:

    • Load the Loom Editor (Monaco-based) with syntax highlighting
    • Capture Threads syntax input and store it in state
    • Render a live preview (even if raw at first)
    • Prepare the folder structure for future parser/renderer/export tools

    We settled on a structure that supports rapid development and scaling:

    /src
      /components    # React UI components
      /parser        # Threads DSL parser (coming soon)
      /renderer      # Threads-to-HTML/JSX renderer
      /storage       # Local storage and persistence
      /export        # Export tools (HTML, JSX, etc.)

    A full engineering ticket was created to scaffold the project using Vite, React, Tailwind, and Monaco. The goal? Allow a user to build a basic layout within 5–10 minutes of opening the editor—no build step required.


    🤖 How AI Helped Me Jumpstart This

    This was a hybrid build:

    • ChatGPT helped me scope the project, break down initial features, naming, and sketch the POC architecture.
    • Claude wrote the initial scaffold.sh script after I gave it our initial ticket for scaffolding out the system architecture (mostly initing, installing packages, and creating a setup.sh) Then starting on an initial implementation to test out the waters.
    • Cursor was used to implement and debug the first parser and build steps interactively.

    I’m using AI as a kind of rubber duck with code generation powers — not to replace my thinking, but to augment it and accelerate the “getting started” pain.


    ✅ Spindle MVP Is Live!

    The MVP is now public at github.com/NickOrtiz/spindle.

    It includes:

    • The Loom Editor with Monaco
    • Project scaffold for future DSL and rendering logic
    • Tailwind-based styling
    • A clean developer-first foundation

    What’s Next:

    • Implement Threads DSL parser (In Progress…)
    • Build layout renderer
    • Add component library
    • Create export functionality
    • Add project management

    🧩 Final Thoughts

    Spindle is just getting started. But it’s designed with intention:

    • Fast for engineers
    • Friendly for component systems
    • Extensible enough to integrate into real production workflows

    If this excites you, check out the GitHub repo, watch for updates, or contribute. I’ll be building this in public.

    🧶 Let’s “spin up” something powerful.

  • Git Bisect – Using a Binary Search Tool to Find A Bugs Initial Occurrence

    Git Bisect – Using a Binary Search Tool to Find A Bugs Initial Occurrence

    git bisect is a powerful tool built into Git that helps identify the specific commit that introduced a bug or regression. It automates a binary search across your commit history, quickly narrowing down to the first “bad” commit.

    Why Use git bisect?

    Use git bisect when:

    • You know a bug exists now that didn’t exist in the past.
    • The bug was introduced somewhere between two known commits.
    • You need to efficiently isolate the cause, especially in large projects or long commit histories.
    • Manual inspection or diffing isn’t feasible due to code complexity or volume.

    How It Works (Conceptual Overview)

    git bisect uses binary search:

    • You mark the current (buggy) commit as bad.
    • You mark a past commit where the bug was not present as good.
    • Git checks out the midpoint between the two.
    • You test it and mark it as good or bad.
    • Git repeats this process until the culprit commit is found.

    Instead of testing every commit (O(n)), it finds the buggy commit in O(log n) time.


    Typical Use Case

    Imagine this scenario:

    • You merged a feature two weeks ago.
    • A bug appears today, but everything worked fine last week.
    • You don’t know which commit caused it.

    Rather than reviewing all 100 commits in that time, git bisect can locate the problematic one in 7–8 steps.


    Step-by-Step Example

    # Start the bisect session
    git bisect start
    
    # Mark the current commit as bad
    git bisect bad
    
    # Mark a known good commit (e.g., a commit hash or tag)
    git bisect good abc1234

    Git will now check out a midpoint commit.

    # You test the code manually or run a test suite
    # If the bug is present:
    git bisect bad
    
    # If the bug is NOT present:
    git bisect good

    Repeat this process. When Git finds the first bad commit:

    # Git will print the bad commit hash
    # To end the session and return to HEAD:
    git bisect reset

    ⚙️ Automating With Scripts

    If the bug can be tested via a script (exit 0 for good, non-zero for bad), automate the process:

    git bisect start
    git bisect bad
    git bisect good abc1234
    git bisect run ./test-for-bug.sh

    Git will automatically run the script at each step and mark commits accordingly.


    🧠 Tips and Gotchas

    • Use clean working states — stash or commit changes before starting.
    • Can be used with rebases, merges, and squashed histories — just ensure you’re referencing commits that existed in history.
    • Works well when you have reproducible bugs.
    • Reset with git bisect reset anytime to return to your original HEAD.

    🔍 When Not To Use It

    Avoid git bisect if:

    • The bug isn’t reproducible or appears inconsistently.
    • There’s no clear “known good” commit.
    • The history is too shallow or the change was introduced in one of a few commits (manual review is faster).

    📌 Related Commands and Concepts

    • git log — Use to find good/bad commit candidates.
    • git show — Inspect individual commits during bisect.
    • git blame — Can be helpful after git bisect to narrow blame within the commit.
    • git cherry-pick — Use if you want to revert or isolate the problematic change after finding it.

    🧭 Closing Thought

    git bisect turns debugging history into a quick session instead of stepping backwards through commits one-by-one until it’s found.

    It’s especially powerful when combined with automated tests — giving you a way to bisect regressions before they reach production.

  • Why I Still Build Sites in WordPress

    Why I Still Build Sites in WordPress

    (Even Though I Could Use Anything Else)

    As a frontend engineer with years of experience across JavaScript frameworks, headless CMS architectures, and static site generators, I often get asked: “Why are you still using WordPress for your own site?”

    It’s a fair question—especially when tools like Next.js, Astro, Sanity, and even Notion-as-CMS are all gaining traction. But after building and maintaining sites for companies ranging from startups to Fortune 100 brands, my answer remains the same:

    WordPress isn’t outdated. It’s evolved—and for the right use case, it still delivers unmatched editorial power, flexibility, and speed.

    Here’s why I still build my site in WordPress—and why you might want to, too.


    1. Editorial UX That Actually Works

    No matter how slick your frontend is, your CMS still needs to be usable. With Gutenberg (WordPress’ block editor), I can empower non-technical users to manage content with real-time visual feedback—no training required.

    For solo creators, teams, or clients, that means:

    • Visual editing without breaking layout integrity.
    • Custom blocks that enforce design consistency.
    • A system built for actual humans—not engineers pretending to be content strategists.

    If you’ve ever had to explain how to “update meta fields in the headless CMS backend and wait for a redeploy,” you know how important this is.


    2. The Block System Scales Beautifully

    I’ve built custom block libraries that are scalable, accessible, and maintainable—without reinventing the wheel. WordPress blocks (especially when structured with theme.json, block variations, and reusable patterns) let me:

    • Create reusable design systems within WordPress.
    • Mirror component-driven development philosophies from React.
    • Avoid bloated page-builder plugins while still offering full flexibility.

    Need custom interactions or a layout that reflects a specific brand identity? You can build it once and let users deploy it anywhere.


    3. Extendability Is Built-in

    WordPress thrives in complexity. I’ve integrated it with CRMs, eCommerce platforms, headless frontends, and APIs with ease.

    More importantly, I can:

    • Write custom PHP for backend logic.
    • Build bespoke REST endpoints or use GraphQL (via WPGraphQL).
    • Hook into nearly anything thanks to WordPress’ mature action/filter architecture.

    For developers who want control without babysitting a million third-party services, this is huge.


    4. It’s Still Ridiculously Fast to Deploy and Update

    From a productivity standpoint, WordPress is hard to beat. I can:

    • Spin up a new site in minutes.
    • Use existing plugins when it makes sense.
    • Apply custom code when it doesn’t.

    You’re not locked into a rigid system—you’re choosing the level of abstraction that makes sense for your current need.

    And with tools like HeadstartWP, you can modernize your stack with Composer, PSR-4 autoloading, and automated deployment pipelines—no spaghetti code required.


    5. It’s Not About Hype—It’s About Fit

    It’s easy to chase the next hot tech. I’ve used React, Svelte, Astro, Eleventy, and others—and they each have their place.

    But when I need:

    • A battle-tested CMS,
    • Solid performance with caching/CDNs,
    • And a client-friendly editing experience,

    WordPress remains a smart, strategic choice—especially when paired with modern frontend practices and a thoughtful development approach.


    💬 Final Thoughts

    I don’t use WordPress because I have to. I use it because it works.
    For me. For clients. For content.

    When built the right way—with accessibility, performance, and UX in mind—WordPress is still one of the most capable, extendable, and reliable tools in a modern developer’s toolkit.

    If you’re a developer thinking about what stack to use for your next project, don’t count WordPress out. You might just find that what you need was here the whole time—just waiting for a modern take.

  • Running DeepSeek-R1 Locally

    Running DeepSeek-R1 Locally

    Install Ollama by downloading the appropriate installer from their website.
    https://ollama.com/download

    After first-run installation it’ll ask you to install your first model, you can use their suggestion or you can install deepseek instead!

    Install Deepseek-R1 using ollama

    ollama run deepseek-r1

    Once this is complete you can run and interact with deepseek in your own local environment

    ollama run deepseek-r1

    If you’re interested in the different deepseek distilled models you can visit the library page on ollama.com: https://ollama.com/library/deepseek-r1

  • Full-Width elements inside Fixed-width containers

    Full-Width elements inside Fixed-width containers

    When working in the frontend, I often come across a common challenge: adding a full-width element inside a fixed-width container. For example, you might need a banner, background section, or hero image to span the full width of the viewport, even when it’s nested inside a parent container with a constrained width.

    The Classic Solution

    One popular CSS “hack” resolves this elegantly:

    .full-width {
      width: 100vw;
      position: relative;
      left: 50%;
      right: 50%;
      margin-left: -50vw;
      margin-right: -50vw;
    }

    Here’s how it works:

    1. width: 100vw: Ensures the element spans the full width of the viewport.
    2. position: relative; left: 50%; right: 50%: Centers the element by offsetting it from the container’s left and right edges.
    3. margin-left: -50vw; margin-right: -50vw: Nudges the element outward by half the viewport width on both sides, canceling out the offsets.

    This approach is simple, effective, and widely used. However, there are alternative methods that may be more appropriate in certain scenarios.


    Alternative Approaches

    1. CSS Grid

    If the parent container uses display: grid, CSS Grid can elegantly achieve the same effect:

    .container {
      display: grid;
      grid-template-columns: 1fr;
    }
    
    .full-width {
      grid-column: 1 / -1;
      width: 100%;
    }
    • Pros: Cleaner implementation with no negative margins.
    • Cons: Requires the parent container to use grid layout, which might not always be feasible.

    2. CSS Transform

    You can also use transform: translateX() to shift the element:

    .full-width {
      width: 100vw;
      position: relative;
      left: 50%;
      transform: translateX(-50%);
    }
    • Pros: Avoids the use of margin-left/margin-right, which can be tricky in some layouts.

    3. Absolutely Positioned Element

    For a more traditional solution, an absolutely positioned element can be placed relative to the viewport:

    .container {
      position: relative;
    }
    
    .full-width {
      position: absolute;
      top: 0;
      left: 0;
      width: 100vw;
    }
    • Pros: Reliable and doesn’t depend on margins or translations.
    • Cons: Requires careful management of parent and child positioning.

    Choosing the Right Approach

    Each method has its use cases:

    • Negative Margins: Best for quick fixes when the parent container’s layout can’t be altered.
    • CSS Grid: Ideal for modern layouts where you can leverage grid properties.
    • CSS Transform: A good choice when animations or transitions are required.
    • Absolute Positioning: Works well for static, standalone elements like headers or banners.

    By understanding the strengths of these options, you can choose the method that best aligns with your layout and project needs.

    What’s your go-to solution for full-width elements? Let me know in the comments below!

  • Generate an SSL Certificate using CertBot

    Generate an SSL Certificate using CertBot

    Certbot is a free, open-source tool that automates the process of obtaining and renewing SSL/TLS certificates from Let’s Encrypt, a certificate authority that provides free SSL certificates. These certificates encrypt the data transmitted between your website and its users, ensuring security and boosting SEO rankings.

    Certbot simplifies what used to be a manual, error-prone process into an automated, streamlined one. It’s widely compatible with popular web servers like Apache and Nginx.


    Steps to Implement Certbot

    1. Install Certbot

    First, ensure your server meets the requirements (e.g., Python 3+). Then, install Certbot using your system’s package manager. For example, on Ubuntu:

    sudo apt update
    sudo apt install certbot python3-certbot-nginx

    For Apache, replace nginx with apache.


    2. Obtain an SSL Certificate

    Run Certbot with your web server plugin. For example, for Nginx:

    sudo certbot --nginx

    Certbot will:

    1. Detect your domain configuration.
    2. Guide you through the setup, including HTTPS redirection.
    3. Automatically configure your web server.

    3. Test Your SSL Certificate

    Verify the certificate was installed correctly by visiting your site with https://. You can also check your configuration with:

    sudo certbot certificates

    4. Automate Renewal

    Certbot automatically schedules certificate renewals, but you can manually test it with:

    sudo certbot renew --dry-run

    This ensures that the renewal process works as expected.


    Why Use Certbot?

    • Free: Provides high-quality SSL certificates at no cost.
    • Automated: Handles both issuance and renewal, minimizing maintenance.
    • Trusted: Certificates are widely accepted by browsers and devices.

    With Certbot, securing your site with HTTPS has never been easier.

    For more information or help visit https://certbot.eff.org/pages/help

  • Getting Started with TensorFlow.js – Real-Time Object Detection

    Getting Started with TensorFlow.js – Real-Time Object Detection

    Ever wondered how object detection works in web applications? With TensorFlow.js, you can leverage pre-trained models to build powerful machine learning applications directly in the browser. In this guide, I’ll walk you through creating a real-time object detection app using TensorFlow.js and the pre-trained Coco-SSD model. This project is beginner-friendly and perfect for exploring the potential of TensorFlow.js.

    What are we building?

    A web-based app that:

    • Accesses your webcam feed.
    • Uses a pre-trained object detection model (Coco-SSD).
    • Displays detected objects in real-time with bounding boxes and labels.

    What is needed?

    • A modern web browser (e.g., Chrome, Edge).
    • Basic JavaScript knowledge.
    • A text editor (vscode or similar) and web server (or just open the HTML file locally).

    The Markup

    Here’s the markup that the code will live in. Minimal styling needed, including our assets for tensorflow.js and coco-ssd, and finally your script.js file where the action lives.

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>TensorFlow Object Detection</title>
        <style>
            body, html {
                margin: 0;
                padding: 0;
                height: 100%;
                overflow: hidden;
            }
    
            canvas {
                position: absolute;
                left: 0;
            }
        </style>
    
        <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
        <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd"></script>
        <script src="script.js">  </script>
    
    </head>
    <body>
        <h1>TensorFlow Object Detection</h1>
    </body>
    </html>
    

    The script

    Here’s the full script we’ll use for object detection. Let’s break it into sections to understand what each part does.

    window.onload = async () => {
      // 1. Create and set up the video element
      const video = document.createElement('video');
      video.width = 640;
      video.height = 480;
      document.body.appendChild(video);
    
      // 2. Create and set up the canvas element
      const canvas = document.createElement('canvas');
      canvas.width = 640;
      canvas.height = 480;
      document.body.appendChild(canvas);
      const ctx = canvas.getContext('2d');
    
      // 3. Access the webcam
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ video: true });
        video.srcObject = stream;
        await video.play();
      } catch (error) {
        console.error('Error accessing the webcam:', error);
        return;
      }
    
      // 4. Load the pre-trained Coco-SSD model
      const model = await cocoSsd.load();
      console.log('Coco-SSD model loaded!');
    
      // 5. Define a function to draw predictions
      function drawPredictions(predictions) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        predictions.forEach((prediction) => {
          const [x, y, width, height] = prediction.bbox;
          ctx.strokeStyle = 'red';
          ctx.lineWidth = 2;
          ctx.strokeRect(x, y, width, height);
          ctx.font = '18px Arial';
          ctx.fillStyle = 'red';
          ctx.fillText(
            `${prediction.class} (${Math.round(prediction.score * 100)}%)`,
            x,
            y > 10 ? y - 5 : 10
          );
        });
      }
    
      // 6. Detect objects and draw predictions in a loop
      async function detectAndDraw() {
        const predictions = await model.detect(video);
        drawPredictions(predictions);
        requestAnimationFrame(detectAndDraw);
      }
    
      // Start the detection loop
      detectAndDraw();
    };
    

    The Breakdown

    1. Set Up the Video and Canvas Elements
      • The video element is used to display the webcam feed.
      • The canvas element acts as an overlay to draw bounding boxes and labels for detected objects. The ctx variable provides a 2D drawing context for the canvas.
    2. Access the Webcam
      • The navigator.mediaDevices.getUserMedia API requests access to the webcam. If successful, the webcam feed is set as the srcObject of the video element.
      • If access is denied or an error occurs, the error is logged to the console.
    3. Load the Coco-SSD Model
      • The cocoSsd.load() function loads the pre-trained object detection model. This model recognizes over 90 object classes, including people, cars, animals, and more.
    4. Draw Predictions
      • The drawPredictions function loops through each detected object and:
        • Draws a bounding box around the detected object.
        • Displays the object’s label and confidence score as text.
    5. Detect and Draw in Real-Time
      • The detectAndDraw function runs the model’s detect method on the video feed to get predictions.
      • It calls drawPredictions to update the canvas with the latest results.
      • The requestAnimationFrame method ensures the detection loop runs smoothly and continuously.

    What’s Happening?

    This project combines TensorFlow.js’s machine learning capabilities with the browser’s native APIs for video and drawing. It’s a lightweight and powerful demonstration of AI in the browser, without requiring any server-side processing.

    Building a real-time object detection app is a rewarding way to get started with TensorFlow.js. This breakdown helps you understand how all the pieces fit together, making it easier to expand or adapt for future projects.

    Further reading:

    References and Resources

    1. TensorFlow.js Documentation
    2. Web APIs
  • Building an Isometric Babylon.js Game: An early Project Scaffold

    Building an Isometric Babylon.js Game: An early Project Scaffold

    In this post, we’ll walk through a simple project scaffold for creating an isometric game using Babylon.js. This scaffold sets up the core elements required to get started with your game development—from initializing the rendering engine to basic player movement.

    Include Babylon.js and its dependencies

    <script src="https://cdn.babylonjs.com/babylon.js"></script>
    <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
    <script src="https://code.jquery.com/pep/0.4.3/pep.js"></script>
    

    Setting Up the Canvas

    The project begins with an HTML canvas element to serve as the rendering target for Babylon.js:

    <canvas id="renderCanvas" touch-action="none"></canvas>

    This canvas is referenced in the JavaScript code to initialize the Babylon.js engine and render the scene. The touch-action attribute is set to “none” to improve touch interaction on mobile devices.

    Some initial styling may be appropriate to add as a default here:

    body, html {
      margin: 0;
      padding: 0;
      height: 100%;
      overflow: hidden;
    }
    
    #renderCanvas {
      width: 100%;
      height: 100%;
      touch-action: none;
    }

    Game Configuration

    Configuration values, like player speed and camera sensitivity, are centralized in a GameConfig object. This simplifies adjustments and allows you to toggle features like debug mode:

    const GameConfig = {
        PLAYER_SPEED: 0.1,
        CAMERA_SENSITIVITY: 0.5,
        WORLD_SCALE: 1,
        DEBUG_MODE: true
    };

    Main Game Class

    The HDTwoDGame class encapsulates all the major systems:

    class HDTwoDGame {
        constructor() {
            this.canvas = document.getElementById("renderCanvas");
            this.engine = null;
            this.scene = null;
            this.camera = null;
            this.player = null;
    
            this.initEngine();
            this.createScene();
            this.setupControls();
            this.setupRendering();
        }
    }

    This object-oriented approach makes the code modular and easier to extend as your game evolves.

    Initializing the Engine

    The initEngine method creates the Babylon.js engine, links it to the canvas, and ensures responsiveness by resizing the engine on window resize events:

    initEngine() {
        this.engine = new BABYLON.Engine(this.canvas, true, {
            preserveDrawingBuffer: true,
            stencil: true
        });
    
        window.addEventListener('resize', () => {
            this.engine.resize();
        });
    }

    Building the Scene

    The createScene method defines the core elements of the game world, including the camera, lighting, and basic geometry:

    Camera Setup

    An isometric-like perspective is achieved using an ArcRotateCamera:

    this.camera = new BABYLON.ArcRotateCamera(
        "MainCamera",
        -Math.PI / 4,  // Horizontal angle
        Math.PI / 4,   // Vertical angle
        10,            // Radius
        new BABYLON.Vector3(0, 0, 0),
        this.scene
    );
    this.camera.attachControl(this.canvas, true);

    Lighting and Geometry

    Soft ambient lighting and a simple ground plane create a minimalistic environment:

    const hemisphericLight = new BABYLON.HemisphericLight(
        "light",
        new BABYLON.Vector3(0, 1, 0),
        this.scene
    );
    hemisphericLight.intensity = 0.7;
    
    const ground = BABYLON.MeshBuilder.CreateGround(
        "ground",
        {width: 10, height: 10},
        this.scene
    );

    Player Placeholder

    A basic box represents the player character:

    this.player = BABYLON.MeshBuilder.CreateBox(
        "player",
        {size: 1},
        this.scene
    );
    this.player.position.y = 0.5;  // Elevate above ground

    Setting Up Controls

    Keyboard controls allow the player to move using WASD keys. The setupControls method listens for keyboard events:

    setupControls() {
        this.scene.onKeyboardObservable.add((kbInfo) => {
            if (kbInfo.type === BABYLON.KeyboardEventTypes.KEYDOWN) {
                this.handleKeyDown(kbInfo.event);
            }
        });
    }
    
    handleKeyDown(evt) {
        const speed = GameConfig.PLAYER_SPEED;
        switch(evt.key.toLowerCase()) {
            case 'w': this.player.position.z -= speed; break; // Forward
            case 's': this.player.position.z += speed; break; // Backward
            case 'a': this.player.position.x -= speed; break; // Left
            case 'd': this.player.position.x += speed; break; // Right
        }
    }

    Rendering the Scene

    The setupRendering method defines the game loop. It updates the camera’s target to follow the player and renders the scene:

    setupRendering() {
        this.engine.runRenderLoop(() => {
            this.camera.target = this.player.position;
            this.scene.render();
        });
    }

    Debugging and Future Extensions

    With GameConfig.DEBUG_MODE enabled, the Babylon.js debug layer can be toggled:

    if (GameConfig.DEBUG_MODE) {
        this.scene.debugLayer.show();
    }

    Conclusion

    This scaffold provides a solid foundation for building a Babylon.js game. It handles essential tasks like engine initialization, scene creation, input handling, and rendering. With these basics in place, you can focus on adding features, improving visuals, and implementing game mechanics.