CSS in City

Detail of the fountain

The illustration of the city above is all CSS. There are no images, no SVG, no JavaScript… there isn’t even any HTML!*

Here’s the source code on CodePen:

(Click on the 1x, 0.5x, and 0.25x buttons to zoom in or out and see more of the illustration. Alternatively, you can also visit the CodePen details page, where you can zoom in/out and resize the window for better results)

You may ask “how did you do it?” And the answer will be “with a lot of patience.” I started this CSS Art two years ago, and worked adding small things little by little: a building here, some tree there, a bus stop close by… But, due to personal and professional reasons, I stopped adding things around a year ago, and haven’t touched the code since. I’d love to expand it into a full city someday.

Basically, it is all a big collection of CSS gradients: radial, linear, conic… I event used the repeating ones. All of them sized and positioned so they fit within a particular spot in the illustration.

This drawing is different from other CSS Art I usually do. I tend to do small things that are responsive, but this is an infinite drawing and it uses absolute units.

Detail of the fountain

It is infinite because it doesn’t matter how much you resize, the illustration just goes on and on. This was achieved by playing with background sizes and positioning, so the same elements repeat ad infinitum. Examples of these elements are:

  • The bricks on the wall
  • The tree tops behind the wall
  • The grayed buildings in the very background
  • The park houses

The absolute units are pixels, and I used that instead of vmin or em, to make sure that all the things will fall exactly in the place I wanted them to be. When using relative units, sometimes something will fall in a half a pixel position and the result looks off.

When drawing and adding elements, I started from the bottom center of the image, and position everything based on that point, growing from there upwards and sideways.

detail of the city

I added some basic animations (but commented them for performance while coding, you can uncomment the code, but it’s not much), so the traffic light actually works, the water in the fountain flows (poorly) or some of the lights flicker. I considered adding a car that stops at the traffic light and an UFO that flies above the city –that won’t be seen unless you zoom out considerably or have a large/tall screen.

And all this using just the :root element. I could still use the ::before or ::after pseudo-elements to do add more things. In particular, animated things. But I dropped the idea because adding them caused the performance to drop considerably. Still, I would like to continue adding more things when I have time.

What do you think? Have you ever done a single div CSS Art?

Illustration of a city

*The CodePen demo may not show any HTML, but it automatically adds a structure that is hidden from view but can be seen reviewing the source code. There is a way to run CSS on the browser without the need to import it from HTML. Unfortunately, it requires updating some settings in the web server, and only one browser supported it last time I checked (Firefox). If we did this, the illustration would be completely CSS only.

When AI writes the code, who remembers WHY?

I’ve been a CTO long enough to have onboarded dozens of developers across teams of varying maturity. The pattern that works has never changed: a new developer reads existing documentation, explores the repository, implements small tasks – often ones designed by a senior to expose how the system’s key features work.

How is the API structured?
Where does state live?
How do data flows connect?

The developer learns by doing, making mistakes, and reflecting on what went wrong.

AI hasn’t killed that process. But poorly thought-through adoption of AI has made it much harder for developers to learn a project and the logic behind it. When a developer delegates everything to an AI assistant without reflection and when AI implements the onboarding task, the developer just submits the PR – the task got done, but the onboarding didn’t happen. The developer no longer learns from mistakes or learns by discovery, because there were no mistakes to learn from and nothing to discover.

That’s not an AI problem. It’s a process problem.

The productivity question

AI tools make developers faster. But the gains aren’t as straightforward as the headlines suggest.

Less experienced developers often see the largest raw productivity gains. They can produce working code faster than ever before. But the quality and sustainability of that output depends almost entirely on the process around them.

With good onboarding, clear guardrails, and access to company context, a developer using AI performs significantly better than one without it. In a less structured environment, the results can go sideways quickly. Developers who haven’t yet built strong engineering instincts may accept any AI suggestion without reflection because they don’t have the experience to question what looks correct but isn’t.

Senior engineers, by contrast, tend to capture a reliable productivity uplift regardless of process maturity. They already have the judgment to validate, reject, or reshape AI output. The difference isn’t the tool. It’s whether the organization has invested in the process to support it.

Three patterns worth watching

These aren’t new failure modes created by AI. They’re existing risks that AI makes faster and less visible.

The reload loop

When AI-generated code breaks, there’s a natural instinct to copy the error back into the chat. A patch arrives. Another error. Repeat. This is a human psychology problem when presented with solutions time after time, people tend to switch off and keep reloading. If something doesn’t work after implementation, it usually signals a gap in project comprehension, not a missing patch. The right response is to step back: trace the data flow, read the related tests, understand the code surrounding the problem. Without that comprehension, both the AI and the developer produce shallow fixes that mask the real issue.

Architecture drift

AI can assist with architectural work, but only when it has proper project context. Without it, developers treat a codebase as a collection of independent prompts rather than a coherent whole. Libraries get introduced for problems that already have internal solutions. Patterns multiply. Data flows become harder to trace. The real challenge is preserving architectural knowledge across AI sessions. Decisions and conventions need to persist between conversations, and that requires deliberate documentation, not just good intentions.

Uncritical acceptance

Developers sometimes accept AI-generated code because it looks right. The output has a confident, professional structure. Developers who haven’t yet built strong debugging instincts may trust polish over correctness, including code with security vulnerabilities. The mitigation is the same as it’s always been – senior review at the PR stage. But it can also be addressed earlier, during refinement, by identifying security-sensitive areas and including specific checks in the task definition.

Across all three patterns, one thing is conspicuously absent from most discussions – automated guardrails. Linters, type checking, CI/CD pipelines, unit tests – these catch entire categories of problems before any human reviews anything. A conversation about AI-assisted development that ignores automated safety nets is incomplete.

What’s actually at risk

Every codebase is an artifact of decisions made under pressure. It contains frameworks chosen after failed experiments, workarounds born from production incidents, edge cases that took months to surface, and security patches whose context lives in decision logs.

What healthy organizations do is build infrastructure that makes context accessible, clean consistent architecture with documentation that explains not just what was built but why; and indexed, discoverable component libraries shared across teams.

Even with all of this, there’s knowledge that lives in places humans struggle to navigate, scattered across repositories, buried in years of commit history, distributed across documentation platforms. This is where AI code intelligence platforms add genuine value: collecting unstructured knowledge from across the organization, auto-documenting code, and serving it as context to AI assistants on demand.

Understanding code before generating

Before writing new code, a developer should be able to answer:

Does a solution to this problem already exist in our codebase?
How have other teams handled the same pattern?
What decisions shaped the module I’m about to modify?

AI assistants can answer some of these questions well, especially within a single, small-to-medium-sized repository. Where it breaks down is across organizational boundaries: multiple repositories, years of accumulated history, cross-team conventions, decisions that were never documented in code. That’s where additional context layers change the equation.

Consider a developer implementing retry logic for a billing service. A general-purpose AI assistant working within the current repo will produce a functional, contextually grounded implementation. But if the best reference implementation lives in a different repository, a RetryStrategy class written by a staff engineer for another service eight months ago, a single-repo tool won’t surface it. A code intelligence platform that indexes across repositories can. The developer gets the class, it’s calling context, the middleware it connects to, and the logging conventions the team adopted.

That’s valuable. But it’s not the end of the story. The solution was created in a different system, and not everything will translate directly. It’s the developer’s job to understand the pattern and work with their AI assistant to adapt it. The platform provides compressed discovery, not compressed implementation. Human understanding remains the critical step.

What actually helps? Building an organizational system

Individual habits like “check if this exists elsewhere” are fine advice, but they rely on personal discipline and don’t scale. What works is building organizational infrastructure that makes good practices the default.

Layer 1: Automated guardrails.

Linters, formatters, type checking, testing strategies. These catch problems before any human reviews anything and encode team standards into automation. This is the foundation, and it costs zero effort per task once configured.

Layer 2: AI configuration infrastructure.

This is where most organizations have untapped potential. AI assistants can be configured with system instructions that encode your team’s conventions, architectural principles, and preferred patterns. Sub-agent definitions and skills libraries can capture tribal knowledge in a form that AI can use during code generation. Written decision logs and architectural guidelines become prompts, not just documents.

Critically, developers need to understand how AI assistant memory works so that decisions and context persist between sessions. Without this, every AI conversation starts from zero and accumulated understanding is lost. Organizations should treat AI session configuration: memory, context documents, progressive context building, as part of their development infrastructure, not as something each developer figures out on their own.

Layer 3: Knowledge aggregation.

Code intelligence platforms indexing across repositories. Documentation platforms connected and searchable. The goal is that when a developer asks “how do we handle this pattern?” the answer draws from the entire organization’s experience, not just the current repo.

Layer 4: Feedback loops.

This is what makes the whole system improve over time rather than decay. Guardrails and linting rules are updated on a regular cadence. Instruction libraries, prompt templates, and skills definitions are refined based on outcomes. Code intelligence platform searches and recommendations improve through feedback. Documentation, tests, and architecture decision records are generated with AI assistance and kept current.

Here’s the key insight: AI both creates the context problem and provides the solution. Writing good documentation, thorough tests, and clear ADRs used to be expensive and time-consuming, so teams skipped it. Now AI makes it fast. We just need to build the infrastructure to capture that context and establish the feedback loops so it improves itself over time.

The real gap

The productivity difference between a new developer and an experienced one exists because experienced engineers have contextual maps built over years. AI can generate output that looks senior-level without building any of that understanding.

When evaluating AI tooling for your team, the right question isn’t “how much code does it generate?” It’s “how much does it help developers understand the system before changing it?”

AI doesn’t replace the journey from new team member to trusted contributor. With the right process: structured onboarding, automated guardrails, well-configured AI tools, organizational knowledge made accessible, and continuous feedback loops; it compresses that journey. Developers onboard in weeks instead of months because they’ve understood the system’s history and patterns, not because they’ve generated their way past them.

Not faster output. Better understanding, at speed.

Thoughts, pushback, and real-world examples welcome in the comments.

I Built a Free Online Image Converter Tool (PNG, JPG, WEBP) – Here’s Why

Developers, designers, and content creators constantly work with images. But converting image formats quickly without installing heavy software can sometimes be frustrating.

So I decided to build a free online image converter tool that allows users to convert images instantly in the browser.

Try it here: convert Tools

Why I Built This Tool

While working on web projects, I frequently needed to:

  • Convert PNG to JPG
  • Convert WEBP to PNG
  • Convert JPG to WEBP
  • Optimize images for faster websites

Most online tools either:

  • Limit file size
  • Require signup
  • Show too many ads
  • Or are slow

So I built a simple, fast, and free converter.

Features

Here are some things I focused on while developing the tool:

Multiple Image Conversions

  • PNG to JPG
  • JPG to PNG
  • WEBP to JPG
  • WEBP to PNG

Fast Processing

Images are processed quickly without complex steps.

No Registration Required

You can convert images instantly without creating an account.

Clean UI

Simple interface so users can convert images in seconds.

Example Use Cases

This tool can be useful for:

Developers

Need to convert assets quickly while building websites.

Bloggers

Convert images before uploading to WordPress or other CMS.

Designers

Change image formats for compatibility.

Students

Quickly convert images for projects or presentations.

Technologies Used

The platform is built with modern web technologies:

  • Next.js
  • React
  • Tailwind CSS
  • Node.js tools

The goal is to create a growing collection of developer utilities and conversion tools.

Future Plans

I’m planning to add more tools soon:

  • Image Resizer
  • PDF to Image
  • Image Compressor
  • Base64 Encoder / Decoder
  • PDF utilities

My goal is to build 100+ free developer and utility tools in one platform.

Feedback Welcome

This project is still evolving. If you have suggestions for improvements or new tools, feel free to share your thoughts.

Also let me know:

What tool do you wish existed but can’t find online?

You can try the tool here:
https://converttool.io

Thanks for reading

webdev

javascript

nextjs

tools

productivity

🚀 Day 26 of My Automation Journey – Conditions, Inputs & Do-While Loop

Today’s session was a mix of core Java fundamentals + tricky logic + real-world usage 💡
We explored how conditions work, how loops behave without braces, user input handling, and even touched JSON/XML basics.

Let’s break it down step by step 👇

🔹 1. Condition Must Be Boolean (True/False)

In Java, every condition must return either true or false.

❌ Invalid Example

int i = 1;
while(i) {   // ❌ Compilation Error
    System.out.println(i);
}

👉 Why error?

  • Java does NOT allow non-boolean conditions
  • Unlike C/C++, numbers are not treated as true/false

✅ Correct Example

int i = 1;
while(i <= 5) {
    System.out.println(i);
    i++;
}

✔ Condition → i <= 5 → returns boolean
✔ Hence valid

🔹 2. True / False Direct Conditions

You can directly use true or false in conditions.

✅ Example

if(true) {
    System.out.println("Always runs");
}

✔ Output:

Always runs

❌ Example

if(false) {
    System.out.println("Never runs");
}

✔ Output:

(no output)

👉 This is useful for testing or debugging

🔹 3. Removing Curly Braces { }

Java allows skipping { } if only one statement is present.

✅ Example

for(int i = 1; i <= 5; i++)
    System.out.println(i);

✔ Output:

1 2 3 4 5

⚠️ Tricky Case

for(int i = 1; i <= 5; i++)
    System.out.println(i);
    System.out.println("Done");

✔ Output:

1 2 3 4 5
Done

👉 Only the first statement is inside loop
👉 Second statement runs once after loop

🔹 4. Infinite Loop (By Default True)

If condition is always true → loop never stops 😱

Example

for(;;) {
    System.out.println("Infinite Loop");
}

👉 This is equivalent to:

while(true) {
    System.out.println("Infinite Loop");
}

✔ Used in:

  • Servers
  • Continuous monitoring systems

🔹 5. break and continue

🔸 break → stops loop completely

for(int i = 1; i <= 5; i++) {
    if(i == 3)
        break;
    System.out.println(i);
}

✔ Output:

1
2

🔸 continue → skips current iteration

for(int i = 1; i <= 5; i++) {
    if(i == 3)
        continue;
    System.out.println(i);
}

✔ Output:

1
2
4
5

🔹 6. Taking Input from User (Scanner)

To read input in Java, we use Scanner.

Example

import java.util.*;

public class Test {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("Enter your name: ");
        String name = sc.nextLine();

        System.out.println("Hello " + name);
    }
}

⚠️ Important Notes

❌ Java does NOT directly support char input
✔ Workaround:

char ch = sc.next().charAt(0);

✔ For String:

String name = sc.nextLine();

🔹 7. Multiple Inputs Example

Scanner sc = new Scanner(System.in);

System.out.print("Enter Tamil mark: ");
int tamil = sc.nextInt();

System.out.print("Enter English mark: ");
int english = sc.nextInt();

int total = tamil + english;

System.out.println("Total mark: " + total);

✔ Output Example:

Enter Tamil mark: 80
Enter English mark: 90
Total mark: 170

🔹 8. JSON vs XML (Basic Understanding)

🔸 JSON (JavaScript Object Notation)

{
  "username": "Manikandan",
  "password": "1234"
}

✔ Lightweight
✔ Easy to read
✔ Mostly used in APIs

🔸 XML (Extensible Markup Language)

<user>
    <username>Manikandan</username>
    <password>1234</password>
</user>

✔ Structured format
✔ Used in older systems / configs

🔹 9. Do-While Loop

👉 Runs at least once, even if condition is false

Example

int i = 1;

do {
    System.out.println(i);
    i++;
} while(i <= 5);

✔ Output:

1 2 3 4 5

🔥 Key Difference

Loop Type Condition Check
while Before execution
do-while After execution

🧠 Key Takeaways

✔ Condition must always be boolean
✔ Java does NOT allow non-boolean conditions
✔ Without {}, only one statement is considered
✔ Infinite loops happen when condition is always true
✔ break → exits loop
✔ continue → skips iteration
✔ Scanner is used for user input
✔ JSON is lightweight, XML is structured
✔ do-while runs at least once

🏁 Conclusion

Today was all about understanding how Java actually behaves under the hood 🧠

These small concepts are very important when:

  • Writing automation scripts
  • Handling test data
  • Building logic-driven frameworks

🔥 Step by step, building strong fundamentals in automation!

🤖 A Small Note
I used ChatGPT to help structure and refine this blog while keeping the concepts aligned with my learning.

Google Apps Script: Powerful for Learning, Limited for Production

Google Apps Script is one of the easiest ways to start building something real with code.

You write a bit of JavaScript, hit deploy, and suddenly you have a working app connected to Google services. No servers, no setup — just pure convenience.

It’s no surprise a lot of beginners (and even experienced devs) get hooked on it.

But at some point, you might start wondering:
“Can I just build everything with this?”

That’s where things start to get interesting.

In this article, we’ll take a closer look at what Google Apps Script is great at — and where it might not be the best choice.

⚡ Why Google Apps Script Feels So Powerful

To be fair, Google Apps Script is incredibly powerful — when used in the right context.

It integrates seamlessly with Google services like Sheets, Drive, and Gmail. You don’t have to think about infrastructure, deployment, or scaling (at least not at the beginning).

⚠️ Where Things Start to Break

The problem isn’t what Google Apps Script can do — it’s how far people try to push it.

As your app grows, you’ll start running into limitations: execution time limits, slower performance, and a lack of flexibility compared to traditional backends.

What works perfectly for a small script or internal tool can quickly become frustrating when more users, more data, or more complexity are involved.

At that point, things don’t scale as smoothly as you might expect.

🚫 It’s Not a Full Backend (And That’s Okay)

Google Apps Script was never meant to replace a full backend framework.

It doesn’t give you the same level of control, performance, or scalability you’d get from something like Node.js or other backend solutions.

And that’s completely fine.

The issue only starts when we expect it to do everything.

✅ When It Actually Makes Sense to Use It

Despite its limitations, Google Apps Script is still a great choice for many use cases:

Automating repetitive tasks
Working with Google Sheets as a lightweight database
Building internal tools
Creating quick prototypes or MVPs
Personal projects

In these scenarios, it’s fast, simple, and incredibly effective.

🚀 A Smarter Way to Use It

Instead of forcing Google Apps Script to handle everything, a better approach is to use it where it shines.

For example, you can use it as a lightweight API or automation layer, while keeping your main application on a more robust stack.

This way, you get the best of both worlds — simplicity where you need it, and scalability where it matters.

🔥 Final Thoughts

Google Apps Script is powerful — but only when used wisely.

It’s an amazing tool for learning, building small projects, and getting things done quickly.

But for larger, production-level applications, choosing the right tools early on can save you a lot of time and frustration.

At the end of the day, it’s not about what a tool can do — it’s about what it’s meant to do.

You can go from idea to working prototype in minutes.

For automation, internal tools, or quick experiments, it honestly feels like a superpower.

Build AI Voice Agent with Gemini 3.1 Flash Live and VideoSDK

Google just launched Gemini 3.1 Flash Live Preview its most capable real-time voice and audio model yet. If you’re building AI voice agents, conversational apps, or anything that needs low-latency audio intelligence, this model is a big deal. And with VideoSDK’s Python SDK, plugging it into your app takes just a few minutes.

In this blog, we’ll walk through what the new model can do, and then build a working voice agent step by step using VideoSDK.

What’s New in Gemini 3.1 Flash Live Preview

Google describes this as its “highest-quality audio and voice model yet,” and there are a few things that actually back that up.

It’s built for real-time, audio-first experiences. Unlike models that convert speech to text and then process it, Gemini 3.1 Flash Live works audio-to-audio meaning it hears you and responds as audio, keeping the conversation feeling natural and fast.

Here’s what stands out:

  • Lower latency than before. Compared to 2.5 Flash Native Audio, this model is noticeably faster. Fewer awkward pauses, snappier responses. That matters a lot when you’re building voice agents where delays break the experience.
  • It actually understands how you say things. The model picks up on acoustic nuances, pitch, pace, tone. So it can tell when you’re asking a casual question vs. when you sound urgent or confused.
  • Better background noise handling. It filters out noise more effectively, which means it works in real environments, not just quiet studios.
  • Multilingual out of the box. Over 90 languages supported for real-time conversations.
  • Longer conversation memory. It can follow the thread of a conversation for twice as long as the previous generation. So your agent won’t “forget” what was said earlier in a long session.
  • Tool use during live conversations. This one is huge for agent builders. The model can now trigger external tools (APIs, functions, searches) while a live conversation is happening not just at the end of a turn.
  • Multimodal awareness. It handles audio and video inputs together, so you can build agents that respond to what they see and hear at the same time.
  • The model ID is: gemini-3.1-flash-live-preview

Building a Voice Agent with VideoSDK

VideoSDK gives you everything you need to wire Gemini 3.1 Flash Live into a real voice application. Here’s how to get set up from scratch.

Step 1 : Create and Activate a Python Virtual Environment

First, create a clean Python environment so your project dependencies stay isolated.

python3 -m venv venv

Activate it:

macOS/Linux

source venv/bin/activate

Windows

venvScriptsactivate

You should see (venv) in your terminal, which means you’re good to go.

Step 2 : Set Up Your Environment Variables

Create a .env file in your project root and add your API keys:

VIDEOSDK_AUTH_TOKEN=your_videosdk_token_here
GOOGLE_API_KEY=your_google_api_key_here

You can get your VideoSDK auth token from the VideoSDK dashboard and your Google API key from Google AI Studio.

Important: when GOOGLE_API_KEY is set in your .env file, do not pass api_key as a parameter in your code the SDK picks it up automatically.

Step 3 : Install the Required Packages

Install VideoSDK’s agents SDK along with the Google plugin:

pip install "videosdk-agents[google]"

Step 4 : Create Your Agent (main.py)

Create a file called main.py in your project folder and paste in the following code:

from videosdk.agents import Agent, AgentSession, Pipeline, JobContext, RoomOptions, WorkerJob
from videosdk.plugins.google import GeminiRealtime, GeminiLiveConfig
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", handlers=[logging.StreamHandler()])

class MyVoiceAgent(Agent):
    def __init__(self):
        super().__init__(
            instructions="You Are VideoSDK's Voice Agent.You are a helpful voice assistant that can answer questions and help with tasks.",
        )

    async def on_enter(self) -> None:
        await self.session.say("Hello, how can I help you today?")

    async def on_exit(self) -> None:
        await self.session.say("Goodbye!")

async def start_session(context: JobContext):
    agent = MyVoiceAgent()
    model = GeminiRealtime(
        model="gemini-3.1-flash-live-preview",
        # When GOOGLE_API_KEY is set in .env - DON'T pass api_key parameter
        # api_key="AIXXXXXXXXXXXXXXXXXXXX", 
        config=GeminiLiveConfig(
            voice="Leda", # Puck, Charon, Kore, Fenrir, Aoede, Leda, Orus, and Zephyr.
            response_modalities=["AUDIO"]
        )
    )

    pipeline = Pipeline(llm=model)
    session = AgentSession(
        agent=agent,
        pipeline=pipeline
    )

    await session.start(wait_for_participant=True, run_until_shutdown=True)

def make_context() -> JobContext:
    room_options = RoomOptions(
        # room_id="<room_id>", # Replace it with your actual room_id
        name="Gemini Realtime Agent",
        playground=True,
    )

    return JobContext(room_options=room_options)

if __name__ == "__main__":
    job = WorkerJob(entrypoint=start_session, jobctx=make_context)
    job.start()

To run the agent:

python main.py

Once you run this command, a playground URL will appear in your terminal. You can use this URL to interact with your AI agent.

What Can You Build With This?

Gemini 3.1 Flash Live + VideoSDK opens up a pretty wide range of real-world use cases:

  • Customer support voice bots. Replace or supplement your call center with agents that actually understand tone and can handle multilingual customers in real time.
  • AI meeting assistants. Agents that join calls, take notes, answer questions from participants, and trigger follow-up actions mid-conversation.
  • Healthcare intake agents. Voice-based triage agents that collect patient information, ask follow-up questions, and route to the right department all in a natural spoken conversation.
  • Language tutors. Real-time conversation partners that catch pronunciation issues, adjust their pace based on the learner, and respond naturally.
  • Voice-controlled IoT and home automation. Agents that listen continuously, understand context, and trigger device actions through tool use all in sub-second response times.
  • Live interview prep tools. Candidates practice answering questions aloud and get spoken feedback instantly.

Conclusion

Gemini 3.1 Flash Live Preview is a meaningful step forward for real-time voice AI. The improvements in latency, noise handling, multilingual support, and especially live tool use make it a strong foundation for production voice agents.

VideoSDK wraps all of that into a clean Python SDK that gets you from zero to a running agent in a handful of lines. Whether you’re prototyping or building something you intend to ship, the setup here gives you a solid starting point.

Next Steps and Resources

Airwave — a self-hosted radio where everyone listens in sync

I built a “shared radio” you can self-host.

Paste a YouTube / Spotify / SoundCloud link →
Airwave turns it into a live stream →
Everyone listens in sync.

No accounts. No premium APIs. No “press play now”.

Runs in Docker:
docker run -p 8000:8000 ghcr.io/76696265636f646572/airwave
Open → http://localhost:8000

Paste a link → music starts → share the URL 🎉

Perfect for:
• parties
• remote teams
• Sonos setups

Github: https://github.com/76696265636f646572/Airwave

How to Protect JavaScript Projects Against Supply Chain Attacks

The Axios incident is a useful reminder that npm risk is not abstract.

A practical defense model for JavaScript projects starts with a few boring but effective controls.

The baseline

Use these defaults:

  • pin versions and keep lockfiles committed
  • run npm ci --ignore-scripts in CI
  • review dependency changes like code changes
  • use short-lived credentials where possible
  • rotate secrets after suspicious installs
  • separate build credentials from runtime credentials

That will not eliminate supply chain risk, but it removes a lot of avoidable exposure.

Why this matters

The main mistake teams make is assuming supply-chain risk is just a vulnerability scanning problem.

It is broader than that.

It is a process problem:

  • install policy
  • dependency review
  • secret scope
  • CI isolation
  • incident response

If those pieces do not work together, a malicious package has a much easier time moving through your system.

What to put in CI

At minimum, I would recommend:

npm ci --ignore-scripts

Then add policy around:

  • lockfile diffs
  • minimum release age rules for critical packages
  • package allowlists for sensitive environments
  • alerts for unexpected dependency ownership changes

Tools like Renovate and Dependabot help, but only if you configure them with policy instead of treating them as passive update bots.

Why the Axios case is useful

The Axios incident is not interesting because it was unique.
It is interesting because it was plausible.

The attack worked because it used the same trust paths teams already rely on every day: a popular package, a maintainer account, and an install-time hook.

That is why the best defenses are often boring, repeatable controls.

A practical checklist

Before you ship, check that you can answer these questions:

  • Who reviews dependency changes?
  • Which builds can execute install scripts?
  • Which secrets are present in CI?
  • What happens if a package becomes compromised tomorrow?
  • How fast can you rotate credentials if that happens?

If those answers are fuzzy, that is where to start.

Originally published on my blog.

DevTrails 2026

Scaling Gig Worker Protection with AI: Our Journey at Guidewire DevTrails 2026

Image

Introduction

Excited to step into the Scale Phase of the Guidewire DevTrails 2026 Hackathon!
After successfully completing the Seed Phase with a 4/5 rating and achieving the *Diamond Tier *, our team is now focused on scaling our solution and bringing it closer to real-world impact.

The Problem We’re Solving

Gig economy workers—especially delivery partners—face unpredictable challenges daily:

  • Extreme weather conditions
  • Air pollution and poor visibility
  • Traffic disruptions and unexpected events

These factors directly impact their earnings, safety, and job stability.

Our Solution: GigCover AI

We built GigCover AI, an AI-powered parametric insurance platform designed specifically for gig workers.

Key Idea:

Instead of traditional insurance claims, payouts are triggered automatically based on real-time risk conditions.

How It Works

  • Collects user location (GPS)
  • Fetches real-time weather data
  • Calculates risk level dynamically
  • Enables instant claim eligibility when risk is high

This ensures faster, fair, and transparent protection.

What We’re Building in the Scale Phase

In this phase, we are focusing on:

  • Enhancing AI models with better predictive analytics
  • Integrating real-time environmental data (weather, pollution, traffic)
  • Designing a scalable system architecture
  • Improving user experience for gig workers
  • Building faster and more transparent parametric triggers

Tackling the “Market Crash” Challenge

One of the most exciting challenges we faced was the Market Crash scenario

A coordinated fraud attack using:

  • Fake GPS locations
  • Multiple accounts
  • Artificial claim triggers

Our Defense Strategy

We designed a multi-layered anti-fraud system:

  • GPS + IP + movement validation
  • Behavioral pattern tracking
  • Device fingerprinting
  • Fraud network detection
  • Risk-based claim control

This ensures:
✔ Fraud detection
✔ Genuine worker protection
✔ System reliability

Tech Stack

  • Frontend (Web): React + Vite + Tailwind CSS
  • Mobile App: Flutter
  • Backend: Flask (Python)
  • Database: SQLite
  • APIs: OpenWeatherMap + REST APIs

Impact

Our platform aims to:

  • Protect gig workers financially
  • Provide instant payouts during risk events
  • Build trust between workers and platforms
  • Deliver real-world social impact

Team – The Core Four

  • Sujitha Rajan
  • Shona B
  • Sharmishthaa R
  • Tarika Paramasivam

Final Thoughts

By combining AI, real-time data, and parametric insurance, we are building a system that is:

  • Innovative
  • Secure
  • Socially impactful

AI #Hackathon #Guidewire #DevTrails2026 #GigEconomy #InsuranceTech

ParametricInsurance #MachineLearning #Flutter #ReactJS #Flask

StartupIdeas #WomenInTech #Innovation #TechForGood #FullStackDevelopment

WeatherAPI #FraudDetection #CyberSecurity #SystemDesign

Building Popverse: What I Learned Designing a Bubble Shooter with 500+ Levels

Bubble shooters are one of those genres that looks solved. Match three, clear the board, repeat. The core mechanic is so well-established that most developers treat it as a template rather than a design problem.

I didn’t want to build a template. I wanted to build something that stayed interesting past level 50.

Why bubble shooters are harder to design than they look
The fundamental challenge with bubble shooters is entropy. Every level starts with a cluttered board and ends with an empty one. That’s the whole game. Once players internalize the basic color-matching mechanic, the only way to maintain engagement is variety — and variety in a bubble shooter is surprisingly difficult to achieve without breaking the feel of the game.

The standard approach is to add obstacles. Frozen bubbles that need multiple hits. Stone bubbles that can’t be destroyed by color matching. Walls that change the bounce angles. These work, but they’re additive complexity — more things to remember, more exceptions to the core rule.

What I found more interesting was multiplicative complexity: elements that interact with each other to produce emergent outcomes the player didn’t fully predict.

Designing 20 special bubble types

Popverse ships with 20 unique special bubble types. Getting to 20 required drawing a hard line between bubbles that added genuine strategic depth and bubbles that just added visual noise.

The ones that made the cut share a property: they change what the optimal move is, not just how hard the move is to execute.

Bomb bubbles are the clearest example. When a Bomb is on the board, the best move is rarely the most obvious color match — it’s often to deliberately set up a chain that detonates the Bomb at the right moment. The presence of a single Bomb bubble reframes the entire board.

Rainbow bubbles work differently. They match any color, which sounds like a pure advantage, but they introduce a decision cost. When you have a Rainbow in your queue, every shot becomes a question: is this the right moment to use it, or should I save it? That friction is the design doing its job.

Lightning bubbles create chain reactions across rows. Portal bubbles teleport shots to unexpected positions. Healer bubbles restore frozen neighbors. Each type introduces a different kind of thinking rather than a different intensity of the same thinking.

The 500+ level problem

500 levels is a lot of content for a solo developer to produce. The practical solution is a level generation philosophy rather than designing each level individually from scratch.

I organized levels into clusters of roughly 15-20, each cluster built around a specific mechanic combination. One cluster focuses on portal bubble navigation. Another introduces Bomb timing under shot pressure. A third combines Frost and Lightning bubbles in ways that force the player to choose between clearing fast and clearing efficiently.

Within each cluster, the first few levels introduce the mechanic cleanly. The middle levels add secondary complications. The final levels remove the scaffolding and let the mechanic run at full difficulty.

This structure means designing 30 clusters rather than 500 individual levels — a much more manageable problem.

What the random puzzle mode taught me

Popverse includes a random puzzle generator alongside the main level progression. I added it as a secondary feature, expecting it to be used occasionally.

Players used it constantly.

The insight this gave me: a significant portion of bubble shooter players aren’t trying to progress through a story. They want an infinite supply of fresh boards to solve. The level progression serves one type of player. The random mode serves another. Both are legitimate, and a game that only ships one of them is leaving half its potential audience underserved.

Technical decisions worth mentioning

Popverse is built with Cocos Creator targeting both browser and mobile from a single codebase. The physics of bubble shooting — arc trajectories, wall bounces, cluster detection — are all handled in TypeScript. Cluster detection in particular is worth getting right early: the algorithm that determines which bubbles to pop after a match affects how satisfying every single shot feels.

The browser version is live at phyfun.com. The iOS version is on the App Store.

If you’re building a puzzle game with a large level count, the most useful thing I can tell you is this: design the system before you design the levels. The system generates the levels. The levels don’t generate themselves.