hero

Claude Code vs Gemini CLI

July 7, 2025

12 min read

On June 25th, Google released the open-source Gemini CLI, bringing its most powerful Gemini 2.5 model straight into the terminal.

Gemini CLI in terminal

Meanwhile, Claude Code by Anthropic has become a go-to option for developers who like working in the terminal. Both tools help you write, fix, and manage code without switching to an editor. But how do they handle real coding tasks?

In this blog, we'll test both on practical problems, from small games to tricky bug fixes, to see how they think, where they struggle, and which one you'd actually want to use.

TL;DR

Claude Code and Gemini CLI are both strong tools for coding from the terminal, but they focus on different things.

Claude Code: Feels more like a developer assistant. It asks smart follow-up questions, gives a cleaner UI, and sometimes builds more than you asked, like full games with effects and animations.

Gemini CLI: Focuses more on utility. It works fast, sticks to the prompt, and is great for quick fixes or shell-level tasks, but the output often feels a bit rushed or too minimal.

Brief on Claude Code

Claude Code is a terminal-based tool by Anthropic that helps you write, read, fix, and commit code, all from the command line. You install it with a single npm command, and start working by typing claude in your terminal.

Claude Code in terminal

It opens up a chat-like prompt where you can ask it to do things like:

> summarize this codebase
> fix the bug in login flow
> create a new component

Claude reads your files directly from the project folder, no need to point it at specific files. It understands the structure and logic pretty well and suggests changes that usually make sense. It won't rush into edits either. Before doing anything, it asks for your approval. That makes it feel safe to use.

You can also use it for Git operations:

> what files have I changed?
> commit with message "Fix auth bug"
> create branch feature/signup-page

Claude Code remembers things across sessions, too. It uses memory files like CLAUDE.md to keep track of your past commands, explanations, or custom instructions. You can manage this memory with simple commands like /memory to see what it knows, or /compact to shrink it down when needed.

For more advanced setups, Claude Code supports MCP (Model Context Protocol). This lets it connect to other tools on your machine, like test runners, databases, or even local APIs. That means you can ask it things like "run the tests" or "query users from local DB" and it can actually do it, no tabs switching.

Claude Code supports /help, session resume (claude -c), and even quick one-liners with claude -p. If you're already a Claude Pro or Max user, you can start using it right away without any extra cost.

It's fast, clean, and works well for most everyday dev tasks, especially if you're already used to working in a terminal.

Brief on Gemini CLI

Gemini CLI is Google's command-line tool that brings its Gemini models directly into your terminal. It works like a smart coding assistant that can read your codebase, help you write and edit files, run shell commands, and automate workflows, all inside your terminal window.

Gemini CLI features

You can start using it instantly with this:

npx https://github.com/google-gemini/gemini-cli

Or install it globally:

npm install -g @google/gemini-cli
gemini

The first time you run it, you'll be asked to sign in with your Google account. That gives you access to 1,000 free requests a day and up to 60 requests per minute. If you want more control or want to choose your model, you can also add a Gemini API key from Google AI Studio.

What makes Gemini CLI stand out is its support for large codebases and complex tasks. It can read multiple files, generate apps from PDFs or screenshots, and even summarize Git history or build reports, just by asking in plain language. It also supports media generation, slide deck creation, and local tool integration through something called MCP (Model Context Protocol), which lets it talk to external tools like file systems or image generators.

Here's the kind of stuff you can ask it:

> Start a new Next.js app with Stripe integration
> Migrate this repo to TypeScript
> Show me all changes from the past 7 days, grouped by feature

You don't need to configure much. Just run the command in your project folder and start typing. It keeps your current files and folders in scope, so you don't have to re-explain things. It feels like talking to someone who's already looking at your code.

What We're Comparing

Claude Code and Gemini CLI both run in your terminal and help with things like editing code, fixing bugs, and managing Git, all without leaving the command line.

But they go about it differently.

So instead of listing features, we'll give them the same real coding tasks and see how each one handles it, how they think, what they miss, and where they shine.

Task 1: Build a Flip Card Memory Game

To test both tools in a quick coding scenario, we asked them to build a flip card memory game. This is the prompt we used:

Create a flip card memory game using plain HTML, CSS, and JavaScript. It should have a 4x4 grid of cards. When a user clicks a card, it should flip. If two cards match, keep them flipped. If not, flip them back after a short delay. Show a win message when all pairs are matched. Keep the design simple and the code clean.

Claude Code:

Claude Code started by asking a few clarifying questions, like "Do you want images, emojis on the cards, or just colors?" Once I confirmed the requirements, it planned the full setup, one HTML file with all the logic inside. This is what Claude comes up with.

The UI looks great, the functionalities are working, and it properly uses the emojis, as I've mentioned.

Code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Memory Flip Card Game</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Arial', sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            color: white;
        }

        .game-header {
            text-align: center;
            margin-bottom: 30px;
        }

        .game-title {
            font-size: 2.5rem;
            margin-bottom: 20px;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }

        .game-stats {
            display: flex;
            gap: 30px;
            font-size: 1.2rem;
            margin-bottom: 10px;
        }

        .stat {
            background: rgba(255,255,255,0.2);
            padding: 10px 20px;
            border-radius: 10px;
            backdrop-filter: blur(10px);
        }

        .game-board {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 15px;
            background: rgba(255,255,255,0.1);
            padding: 25px;
            border-radius: 20px;
            backdrop-filter: blur(10px);
            box-shadow: 0 8px 32px rgba(0,0,0,0.2);
        }

        .card {
            width: 80px;
            height: 80px;
            position: relative;
            cursor: pointer;
            perspective: 1000px;
        }

        .card-inner {
            width: 100%;
            height: 100%;
            position: relative;
            transform-style: preserve-3d;
            transition: transform 0.6s;
        }

        .card.flipped .card-inner {
            transform: rotateY(180deg);
        }

        .card-front, .card-back {
            position: absolute;
            width: 100%;
            height: 100%;
            backface-visibility: hidden;
            border-radius: 12px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.8rem;
            font-weight: bold;
            box-shadow: 0 4px 8px rgba(0,0,0,0.2);
        }

        .card-front {
            background: linear-gradient(45deg, #4facfe 0%, #00f2fe 100%);
            color: white;
        }

        .card-back {
            background: white;
            color: #333;
            transform: rotateY(180deg);
        }

        .card.matched {
            opacity: 0.7;
            cursor: default;
        }

        .card.matched .card-inner {
            transform: rotateY(180deg);
        }

        .reset-btn {
            margin-top: 20px;
            padding: 12px 24px;
            font-size: 1.1rem;
            background: rgba(255,255,255,0.2);
            color: white;
            border: none;
            border-radius: 10px;
            cursor: pointer;
            transition: all 0.3s;
            backdrop-filter: blur(10px);
        }

        .reset-btn:hover {
            background: rgba(255,255,255,0.3);
            transform: translateY(-2px);
        }

        .message {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: white;
            color: #333;
            padding: 30px;
            border-radius: 15px;
            text-align: center;
            box-shadow: 0 10px 30px rgba(0,0,0,0.3);
            z-index: 1000;
            display: none;
        }

        .message h2 {
            margin-bottom: 15px;
            color: #667eea;
        }

        .message button {
            margin-top: 15px;
            padding: 10px 20px;
            background: #667eea;
            color: white;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            font-size: 1rem;
        }

        .overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.5);
            z-index: 999;
            display: none;
        }

        @media (max-width: 480px) {
            .card {
                width: 60px;
                height: 60px;
            }
            
            .card-front, .card-back {
                font-size: 1.4rem;
            }
            
            .game-stats {
                flex-direction: column;
                gap: 10px;
            }
        }
    </style>
</head>
<body>
    <div class="game-header">
        <h1 class="game-title">Memory Game</h1>
        <div class="game-stats">
            <div class="stat">Time: <span id="timer">6:00</span></div>
            <div class="stat">Moves: <span id="moves">0</span></div>
            <div class="stat">Matches: <span id="matches">0</span>/8</div>
        </div>
    </div>

    <div class="game-board" id="gameBoard"></div>
    
    <button class="reset-btn" onclick="resetGame()">New Game</button>

    <div class="overlay" id="overlay"></div>
    <div class="message" id="message">
        <h2 id="messageTitle"></h2>
        <p id="messageText"></p>
        <button onclick="hideMessage()">Play Again</button>
    </div>

    <script>
        let gameState = {
            cards: [],
            flippedCards: [],
            matchedPairs: 0,
            moves: 0,
            timeLeft: 360, // 6 minutes in seconds
            gameActive: true,
            timer: null
        };

        const symbols = ['🎮', '🎯', '🎨', '🎪', '🎭', '🎸', '🎺', '🎻'];
        
        function initGame() {
            // Reset game state
            gameState = {
                cards: [],
                flippedCards: [],
                matchedPairs: 0,
                moves: 0,
                timeLeft: 360,
                gameActive: true,
                timer: null
            };

            // Create card pairs and shuffle
            const cardSymbols = [...symbols, ...symbols];
            shuffle(cardSymbols);

            // Create cards
            gameState.cards = cardSymbols.map((symbol, index) => ({
                id: index,
                symbol: symbol,
                isFlipped: false,
                isMatched: false
            }));

            renderBoard();
            updateDisplay();
            startTimer();
        }

        function shuffle(array) {
            for (let i = array.length - 1; i > 0; i--) {
                const j = Math.floor(Math.random() * (i + 1));
                [array[i], array[j]] = [array[j], array[i]];
            }
        }

        function renderBoard() {
            const gameBoard = document.getElementById('gameBoard');
            gameBoard.innerHTML = '';

            gameState.cards.forEach(card => {
                const cardElement = document.createElement('div');
                cardElement.className = 'card';
                cardElement.dataset.cardId = card.id;
                cardElement.onclick = () => flipCard(card.id);

                cardElement.innerHTML = `
                    <div class="card-inner">
                        <div class="card-front">?</div>
                        <div class="card-back">${card.symbol}</div>
                    </div>
                `;

                gameBoard.appendChild(cardElement);
            });
        }

        function flipCard(cardId) {
            if (!gameState.gameActive) return;
            
            const card = gameState.cards[cardId];
            const cardElement = document.querySelector(`[data-card-id="${cardId}"]`);
            
            // Can't flip if already flipped or matched, or if two cards are already flipped
            if (card.isFlipped || card.isMatched || gameState.flippedCards.length >= 2) {
                return;
            }

            // Flip the card
            card.isFlipped = true;
            cardElement.classList.add('flipped');
            gameState.flippedCards.push(cardId);

            // Check for match when two cards are flipped
            if (gameState.flippedCards.length === 2) {
                gameState.moves++;
                updateDisplay();
                
                setTimeout(() => {
                    checkMatch();
                }, 800);
            }
        }

        function checkMatch() {
            const [firstId, secondId] = gameState.flippedCards;
            const firstCard = gameState.cards[firstId];
            const secondCard = gameState.cards[secondId];

            if (firstCard.symbol === secondCard.symbol) {
                // Match found
                firstCard.isMatched = true;
                secondCard.isMatched = true;
                
                document.querySelector(`[data-card-id="${firstId}"]`).classList.add('matched');
                document.querySelector(`[data-card-id="${secondId}"]`).classList.add('matched');
                
                gameState.matchedPairs++;
                
                // Check win condition
                if (gameState.matchedPairs === 8) {
                    endGame(true);
                }
            } else {
                // No match - flip back
                firstCard.isFlipped = false;
                secondCard.isFlipped = false;
                
                document.querySelector(`[data-card-id="${firstId}"]`).classList.remove('flipped');
                document.querySelector(`[data-card-id="${secondId}"]`).classList.remove('flipped');
            }

            gameState.flippedCards = [];
            updateDisplay();
        }

        function startTimer() {
            gameState.timer = setInterval(() => {
                gameState.timeLeft--;
                updateDisplay();
                
                if (gameState.timeLeft <= 0) {
                    endGame(false);
                }
            }, 1000);
        }

        function updateDisplay() {
            const minutes = Math.floor(gameState.timeLeft / 60);
            const seconds = gameState.timeLeft % 60;
            document.getElementById('timer').textContent = 
                `${minutes}:${seconds.toString().padStart(2, '0')}`;
            
            document.getElementById('moves').textContent = gameState.moves;
            document.getElementById('matches').textContent = `${gameState.matchedPairs}/8`;
        }

        function endGame(won) {
            gameState.gameActive = false;
            clearInterval(gameState.timer);
            
            const messageTitle = document.getElementById('messageTitle');
            const messageText = document.getElementById('messageText');
            
            if (won) {
                const timeUsed = 360 - gameState.timeLeft;
                const minutes = Math.floor(timeUsed / 60);
                const seconds = timeUsed % 60;
                
                messageTitle.textContent = '🎉 Congratulations!';
                messageText.innerHTML = `
                    You won!<br>
                    Time: ${minutes}:${seconds.toString().padStart(2, '0')}<br>
                    Moves: ${gameState.moves}<br>
                    Score: ${Math.max(1000 - gameState.moves * 10 - timeUsed * 2, 100)}
                `;
            } else {
                messageTitle.textContent = '⏰ Time\'s Up!';
                messageText.innerHTML = `
                    Game Over!<br>
                    Matches: ${gameState.matchedPairs}/8<br>
                    Moves: ${gameState.moves}<br>
                    Score: ${gameState.matchedPairs * 100}
                `;
            }
            
            showMessage();
        }

        function showMessage() {
            document.getElementById('overlay').style.display = 'block';
            document.getElementById('message').style.display = 'block';
        }

        function hideMessage() {
            document.getElementById('overlay').style.display = 'none';
            document.getElementById('message').style.display = 'none';
            resetGame();
        }

        function resetGame() {
            clearInterval(gameState.timer);
            initGame();
        }

        // Start the game when page loads
        initGame();
    </script>
</body>
</html>

Gemini CLI:

I used the same prompt with Gemini CLI, and it came up with this result:

The UI looks completely wrong. There is no styling, and all the cards are in a straight line, making everything look bad.

Code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Memory Game</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>Memory Game</h1>
    <div class="game-info">
        <div class="score">Score: <span id="score">0</span></div>
        <div class="timer">Time: <span id="timer">360</span>s</div>
    </div>
    <div class="game-container">
        <div class="game-board" id="game-board"></div>
    </div>
    <div id="win-message" class="win-message">
        <h2>You Won!</h2>
        <p>Your final score is: <span id="final-score"></span></p>
        <button id="restart-button">Play Again</button>
    </div>
    <script src="script.js"></script>
</body>
</html>

Task 2: Fixing the Drag & Drop Reorder List

We gave both tools a buggy version of a task list app. Items could be dragged, but dropping was inconsistent and often broke if you didn't land exactly on a list item.

This is the code that requires fixing.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Broken Task Reorder</title>
  <style>
    body {
      font-family: sans-serif;
      background: #f2f2f2;
      padding: 2rem;
    }

    ul {
      list-style: none;
      padding: 0;
      width: 300px;
      background: #fff;
      border-radius: 8px;
      box-shadow: 0 1px 6px rgba(0,0,0,0.1);
    }

    li {
      padding: 1rem;
      border-bottom: 1px solid #ddd;
      background: #fff;
    }

    li.dragging {
      opacity: 0.5;
    }
  </style>
</head>
<body>

  <ul id="taskList">
    <li draggable="true">✅ Wash dishes</li>
    <li draggable="true">📝 Do homework</li>
    <li draggable="true">📞 Call friend</li>
    <li draggable="true">🛒 Buy groceries</li>
  </ul>

  <script>
    const taskList = document.getElementById('taskList');
    let dragged;

    taskList.addEventListener('dragstart', (e) => {
      dragged = e.target;
      e.target.classList.add('dragging');
    });

    taskList.addEventListener('dragend', (e) => {
      e.target.classList.remove('dragging');
    });

    taskList.addEventListener('dragover', (e) => {
      e.preventDefault();
    });

    taskList.addEventListener('drop', (e) => {
      e.preventDefault();
      const target = e.target;

      // ❌ BUG 1: Drops on inner text instead of list item
      if (target !== dragged && target.tagName === 'LI') {
        taskList.insertBefore(dragged, target.nextSibling);
      }
    });
  </script>

</body>
</html>

I gave this prompt to each one:

Review this drag and drop list code. It looks fine but behaves oddly when moving items around. Sometimes it drops in the wrong spot or doesn't respond at all. Fix the bugs and make the dragging smoother and more reliable.

Gemini CLI:

To test with Gemini, we just need to tag that file using `@` sign and it will be able to read and update that file. This is what Gemini came up with.

Gemini kept it simple and reliable. It fixed all drop position issues using `getDragAfterElement`, added clean animations, and gave the interface a nice modern touch. The logic is clear, short, and easy to follow. It's exactly the kind of fix you'd expect from a developer trying to patch something quickly but cleanly.

Code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Smooth Drag-and-Drop List</title>
    <style>
      body {
          font-family: 'Arial', sans-serif;
          background: linear-gradient(to right, #8e9eab, #eef2f3);
          padding: 3rem;
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          min-height: 100vh;
          margin: 0;
      }

      h1 {
          color: #333;
          margin-bottom: 2rem;
      }

      ul {
          list-style: none;
          padding: 0;
          width: 350px;
          background: #fff;
          border-radius: 10px;
          box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
          overflow: hidden; /* Ensures li borders don't stick out */
      }

      li {
          padding: 1rem 1.5rem;
          border-bottom: 1px solid #eee;
          background: #fff;
          cursor: move;
          transition: background-color 0.3s, transform 0.2s ease-in-out;
          user-select: none; /* Prevents text selection while dragging */
      }

      li:last-child {
          border-bottom: none;
      }

      li:hover {
          background-color: #f9f9f9;
      }

      /* Style for the item being dragged */
      li.dragging {
          opacity: 0.4;
          background: #cce5ff; /* A light blue to indicate dragging */
          transform: scale(1.05);
          box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
      }

      /* Visual cue for where the item will be dropped */
      .drag-over {
          border-top: 2px solid #3498db;
      }
    </style>
</head>
<body>
    <h1>Task List</h1>
    <ul id="taskList">
      <li draggable="true">✅ Wash dishes</li>
      <li draggable="true">📝 Do homework</li>
      <li draggable="true">📞 Call friend</li>
      <li draggable="true">🛒 Buy groceries</li>
    </ul>

    <script>
      const taskList = document.getElementById("taskList");
      let draggedItem = null;

      taskList.addEventListener("dragstart", (e) => {
          draggedItem = e.target;
          // Use setTimeout to allow the browser to paint the original element before we style it
          setTimeout(() => {
              e.target.classList.add("dragging");
          }, 0);
      });

      taskList.addEventListener("dragend", (e) => {
          setTimeout(() => {
              e.target.classList.remove("dragging");
              draggedItem = null;
              // Clean up any leftover visual cues
              document.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over'));
          }, 0);
      });

      taskList.addEventListener("dragover", (e) => {
          e.preventDefault();
          const afterElement = getDragAfterElement(taskList, e.clientY);
          
          // Clean up previous cues
          document.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over'));

          if (afterElement) {
              // Add visual cue to the element we are inserting before
              afterElement.classList.add('drag-over');
          }
      });

      taskList.addEventListener("drop", (e) => {
          e.preventDefault();
          const afterElement = getDragAfterElement(taskList, e.clientY);
          if (afterElement == null) {
              taskList.appendChild(draggedItem);
          } else {
              taskList.insertBefore(draggedItem, afterElement);
          }
          // Clean up visual cues
          document.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over'));
      });

      function getDragAfterElement(container, y) {
          const draggableElements = [...container.querySelectorAll("li:not(.dragging)")];

          return draggableElements.reduce(
              (closest, child) => {
                  const box = child.getBoundingClientRect();
                  const offset = y - box.top - box.height / 2;
                  if (offset < 0 && offset > closest.offset) {
                      return { offset: offset, element: child };
                  } else {
                      return closest;
                  }
              },
              { offset: Number.NEGATIVE_INFINITY }
          ).element;
      }
    </script>
</body>
</html>

Claude Code:

Claude also followed the same approach. We mentioned the file first, and then it is able to understand and is ready to make changes. It came up with this result.

Claude went a few steps further. It not only fixed the bugs but also built a mini drag-and-drop framework with smooth transitions, shadows, and even touch support. It added a button to generate random tasks with emojis and reorder them. It's a fuller UI/UX revamp, not just a patch.

In fact, this type of behavior, going beyond just "fixing" and truly understanding the UX intent, is exactly what Entelligence aims to do at scale. If you work with larger codebases or want to track the development of features week by week, Entelligence can provide insights at the repository level, generate comprehensive documentation, and identify architectural patterns without requiring you to explain everything from scratch.


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Enhanced Task Reorder</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 2rem;
        }

        .container {
            background: rgba(255, 255, 255, 0.95);
            backdrop-filter: blur(20px);
            border-radius: 20px;
            padding: 2rem;
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
            border: 1px solid rgba(255, 255, 255, 0.2);
            max-width: 400px;
            width: 100%;
        }

        .header {
            text-align: center;
            margin-bottom: 2rem;
        }

        .header h1 {
            color: #333;
            font-size: 2rem;
            font-weight: 700;
            margin-bottom: 0.5rem;
            background: linear-gradient(135deg, #667eea, #764ba2);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }

        .header p {
            color: #666;
            font-size: 0.9rem;
            opacity: 0.8;
        }

        .task-list {
            list-style: none;
            padding: 0;
            margin: 0;
            min-height: 200px;
            position: relative;
        }

        .task-item {
            background: linear-gradient(135deg, #f8f9ff 0%, #ffffff 100%);
            margin-bottom: 0.75rem;
            padding: 1.25rem 1.5rem;
            border-radius: 15px;
            cursor: grab;
            user-select: none;
            border: 2px solid transparent;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
            position: relative;
            overflow: hidden;
        }

        .task-item::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: linear-gradient(135deg, #667eea, #764ba2);
            opacity: 0;
            transition: opacity 0.3s ease;
            z-index: -1;
        }

        .task-item:hover {
            transform: translateY(-2px);
            box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
            border-color: rgba(102, 126, 234, 0.3);
        }

        .task-item:hover::before {
            opacity: 0.1;
        }

        .task-item:active {
            cursor: grabbing;
        }

        .task-item.dragging {
            opacity: 0.6;
            transform: scale(1.05) rotate(3deg);
            z-index: 1000;
            box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2);
            border-color: #667eea;
        }

        .task-item.dragging::before {
            opacity: 0.2;
        }

        .task-content {
            display: flex;
            align-items: center;
            gap: 0.75rem;
            font-size: 1rem;
            color: #333;
            font-weight: 500;
            z-index: 1;
            position: relative;
        }

        .task-emoji {
            font-size: 1.2rem;
            flex-shrink: 0;
        }

        .task-text {
            flex: 1;
        }

        .drag-handle {
            opacity: 0;
            transition: opacity 0.2s ease;
            color: #999;
            font-size: 1.2rem;
            cursor: grab;
        }

        .task-item:hover .drag-handle {
            opacity: 1;
        }

        .drop-indicator {
            height: 3px;
            background: linear-gradient(90deg, #667eea, #764ba2);
            border-radius: 2px;
            margin: 0.5rem 0;
            opacity: 0;
            transform: scaleX(0);
            transition: all 0.2s ease;
            box-shadow: 0 0 10px rgba(102, 126, 234, 0.5);
        }

        .drop-indicator.active {
            opacity: 1;
            transform: scaleX(1);
        }

        .task-list.drag-active .task-item:not(.dragging) {
            transition: transform 0.2s ease;
        }

        .task-list.drag-active .task-item.drag-over {
            transform: translateY(8px);
        }

        .add-task {
            margin-top: 1rem;
            padding: 0.75rem 1.5rem;
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            border: none;
            border-radius: 12px;
            font-size: 0.9rem;
            cursor: pointer;
            transition: all 0.3s ease;
            width: 100%;
            font-weight: 600;
        }

        .add-task:hover {
            transform: translateY(-2px);
            box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);
        }

        @media (max-width: 480px) {
            .container {
                margin: 1rem;
                padding: 1.5rem;
            }
            
            .header h1 {
                font-size: 1.5rem;
            }
        }

        /* Smooth animations for reordering */
        @keyframes slideIn {
            from {
                opacity: 0;
                transform: translateY(-20px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .task-item.new-item {
            animation: slideIn 0.3s ease-out;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>✨ Task Master</h1>
            <p>Drag and drop to reorder your tasks</p>
        </div>
        
        <ul class="task-list" id="taskList">
            <li class="task-item" draggable="true">
                <div class="task-content">
                    <span class="task-emoji">✅</span>
                    <span class="task-text">Wash dishes</span>
                    <span class="drag-handle">⋮⋮</span>
                </div>
            </li>
            <li class="task-item" draggable="true">
                <div class="task-content">
                    <span class="task-emoji">📝</span>
                    <span class="task-text">Do homework</span>
                    <span class="drag-handle">⋮⋮</span>
                </div>
            </li>
            <li class="task-item" draggable="true">
                <div class="task-content">
                    <span class="task-emoji">📞</span>
                    <span class="task-text">Call friend</span>
                    <span class="drag-handle">⋮⋮</span>
                </div>
            </li>
            <li class="task-item" draggable="true">
                <div class="task-content">
                    <span class="task-emoji">🛒</span>
                    <span class="task-text">Buy groceries</span>
                    <span class="drag-handle">⋮⋮</span>
                </div>
            </li>
            <li class="task-item" draggable="true">
                <div class="task-content">
                    <span class="task-emoji">🏃</span>
                    <span class="task-text">Go for a run</span>
                    <span class="drag-handle">⋮⋮</span>
                </div>
            </li>
        </ul>
        
        <button class="add-task" onclick="addNewTask()">+ Add New Task</button>
    </div>

    <script>
        class TaskReorder {
            constructor(listElement) {
                this.list = listElement;
                this.draggedItem = null;
                this.placeholder = null;
                this.isDragging = false;
                
                this.init();
            }
            
            init() {
                this.list.addEventListener('dragstart', this.handleDragStart.bind(this));
                this.list.addEventListener('dragend', this.handleDragEnd.bind(this));
                this.list.addEventListener('dragover', this.handleDragOver.bind(this));
                this.list.addEventListener('dragenter', this.handleDragEnter.bind(this));
                this.list.addEventListener('dragleave', this.handleDragLeave.bind(this));
                this.list.addEventListener('drop', this.handleDrop.bind(this));
                
                // Add touch support for mobile
                this.addTouchSupport();
            }
            
            handleDragStart(e) {
                if (!e.target.classList.contains('task-item')) return;
                
                this.draggedItem = e.target;
                this.isDragging = true;
                
                e.target.classList.add('dragging');
                this.list.classList.add('drag-active');
                
                // Set drag data
                e.dataTransfer.effectAllowed = 'move';
                e.dataTransfer.setData('text/html', e.target.outerHTML);
                
                // Create placeholder
                this.createPlaceholder();
            }
            
            handleDragEnd(e) {
                if (!e.target.classList.contains('task-item')) return;
                
                e.target.classList.remove('dragging');
                this.list.classList.remove('drag-active');
                
                // Clean up
                this.removePlaceholder();
                this.clearDragOverEffects();
                
                this.draggedItem = null;
                this.isDragging = false;
            }
            
            handleDragOver(e) {
                if (!this.isDragging) return;
                
                e.preventDefault();
                e.dataTransfer.dropEffect = 'move';
                
                const afterElement = this.getDragAfterElement(e.clientY);
                const draggedItem = this.draggedItem;
                
                if (afterElement == null) {
                    this.list.appendChild(draggedItem);
                } else {
                    this.list.insertBefore(draggedItem, afterElement);
                }
            }
            
            handleDragEnter(e) {
                e.preventDefault();
            }
            
            handleDragLeave(e) {
                // Only remove effects if we're leaving the list entirely
                if (!this.list.contains(e.relatedTarget)) {
                    this.clearDragOverEffects();
                }
            }
            
            handleDrop(e) {
                if (!this.isDragging) return;
                
                e.preventDefault();
                this.clearDragOverEffects();
            }
            
            getDragAfterElement(y) {
                const draggableElements = [...this.list.querySelectorAll('.task-item:not(.dragging)')];
                
                return draggableElements.reduce((closest, child) => {
                    const box = child.getBoundingClientRect();
                    const offset = y - box.top - box.height / 2;
                    
                    if (offset < 0 && offset > closest.offset) {
                        return { offset: offset, element: child };
                    } else {
                        return closest;
                    }
                }, { offset: Number.NEGATIVE_INFINITY }).element;
            }
            
            createPlaceholder() {
                this.placeholder = document.createElement('div');
                this.placeholder.className = 'drop-indicator';
                this.placeholder.style.height = '3px';
            }
            
            removePlaceholder() {
                if (this.placeholder && this.placeholder.parentNode) {
                    this.placeholder.parentNode.removeChild(this.placeholder);
                }
            }
            
            clearDragOverEffects() {
                const items = this.list.querySelectorAll('.task-item');
                items.forEach(item => item.classList.remove('drag-over'));
            }
            
            addTouchSupport() {
                let touchItem = null;
                let touchStartY = 0;
                let touchStartX = 0;
                let isTouchDragging = false;
                
                this.list.addEventListener('touchstart', (e) => {
                    if (!e.target.closest('.task-item')) return;
                    
                    touchItem = e.target.closest('.task-item');
                    const touch = e.touches[0];
                    touchStartX = touch.clientX;
                    touchStartY = touch.clientY;
                    
                    // Add a small delay to distinguish between scroll and drag
                    setTimeout(() => {
                        if (touchItem) {
                            touchItem.style.transition = 'none';
                        }
                    }, 100);
                }, { passive: true });
                
                this.list.addEventListener('touchmove', (e) => {
                    if (!touchItem) return;
                    
                    const touch = e.touches[0];
                    const deltaX = Math.abs(touch.clientX - touchStartX);
                    const deltaY = Math.abs(touch.clientY - touchStartY);
                    
                    // Start dragging if moved more than 10px
                    if (deltaY > 10 && deltaY > deltaX && !isTouchDragging) {
                        e.preventDefault();
                        isTouchDragging = true;
                        touchItem.classList.add('dragging');
                        this.list.classList.add('drag-active');
                        this.draggedItem = touchItem;
                        this.isDragging = true;
                    }
                    
                    if (isTouchDragging) {
                        e.preventDefault();
                        
                        // Move the item visually
                        touchItem.style.transform = `translateY(${touch.clientY - touchStartY}px)`;
                        
                        // Find the element we're hovering over
                        const elementBelow = document.elementFromPoint(touch.clientX, touch.clientY);
                        const targetItem = elementBelow?.closest('.task-item');
                        
                        if (targetItem && targetItem !== touchItem) {
                            const afterElement = this.getDragAfterElement(touch.clientY);
                            if (afterElement == null) {
                                this.list.appendChild(touchItem);
                            } else {
                                this.list.insertBefore(touchItem, afterElement);
                            }
                        }
                    }
                }, { passive: false });
                
                this.list.addEventListener('touchend', () => {
                    if (touchItem) {
                        touchItem.style.transition = '';
                        touchItem.style.transform = '';
                        touchItem.classList.remove('dragging');
                        this.list.classList.remove('drag-active');
                    }
                    
                    touchItem = null;
                    isTouchDragging = false;
                    this.isDragging = false;
                    this.draggedItem = null;
                });
            }
        }
        
        // Initialize the task reorder functionality
        const taskList = document.getElementById('taskList');
        new TaskReorder(taskList);
        
        // Add new task functionality
        function addNewTask() {
            const emojis = ['🎯', '💪', '🌟', '🚀', '💡', '🎨', '📚', '🎵', '🌱', '⚡'];
            const tasks = [
                'Learn something new',
                'Exercise for 30 minutes',
                'Organize workspace',
                'Plan weekend activities',
                'Read a chapter',
                'Practice meditation',
                'Call family',
                'Update resume',
                'Clean room',
                'Drink more water'
            ];
            
            const randomEmoji = emojis[Math.floor(Math.random() * emojis.length)];
            const randomTask = tasks[Math.floor(Math.random() * tasks.length)];
            
            const newItem = document.createElement('li');
            newItem.className = 'task-item new-item';
            newItem.draggable = true;
            newItem.innerHTML = `
                <div class="task-content">
                    <span class="task-emoji">${randomEmoji}</span>
                    <span class="task-text">${randomTask}</span>
                    <span class="drag-handle">⋮⋮</span>
                </div>
            `;
            
            taskList.appendChild(newItem);
            
            // Remove the animation class after animation completes
            setTimeout(() => {
                newItem.classList.remove('new-item');
            }, 300);
        }
    </script>
</body>
</html>

Task 3: Click-to-Shoot in 3D Space

We gave both tools a slightly more advanced challenge: create a simple shooting game in 3D space.

We've used this prompt:

Build a simple 3D shooter using Three.js.
In the scene, place some floating targets (like cubes or spheres) in random positions. The user should be able to click with the mouse to "shoot" them. When a target is hit, it should disappear or react visually (like change color or explode).
Use raycasting from the mouse to detect the hit. Add a basic camera and lighting setup. Make sure the interaction feels smooth and responsive. Keep the code modular and all inside one HTML file.

Gemini CLI:

Gemini built a working 3D scene with static shapes using Three.js. Clicking on them changed their color, but that's where the interaction ended. There was no animation, no feedback, and no sense of a real game, just clickable shapes sitting still in space.

Code:


<!DOCTYPE html>
<html lang="en">
<head>
    <title>3D Shooter</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        // Scene setup
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // Lighting
        const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
        scene.add(ambientLight);
        const pointLight = new THREE.PointLight(0xffffff, 0.5);
        pointLight.position.set(5, 5, 5);
        scene.add(pointLight);

        // Targets
        const targets = [];
        const targetGeometry = new THREE.SphereGeometry(0.5, 32, 32);
        for (let i = 0; i < 20; i++) {
            const targetMaterial = new THREE.MeshStandardMaterial({ color: Math.random() * 0xffffff });
            const target = new THREE.Mesh(targetGeometry, targetMaterial);
            target.position.x = (Math.random() - 0.5) * 20;
            target.position.y = (Math.random() - 0.5) * 20;
            target.position.z = (Math.random() - 0.5) * 20 - 5;
            scene.add(target);
            targets.push(target);
        }

        // Camera position
        camera.position.z = 30;

        // Raycasting
        const raycaster = new THREE.Raycaster();
        const mouse = new THREE.Vector2();

        function onMouseClick(event) {
            // Calculate mouse position in normalized device coordinates
            // (-1 to +1) for both components
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
            mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;

  // Update the picking ray with the camera and mouse position
  raycaster.setFromCamera(mouse, camera);

            // Calculate objects intersecting the picking ray
            const intersects = raycaster.intersectObjects(targets);

            if (intersects.length > 0) {
                const intersectedObject = intersects[0].object;
                // Change color on hit
                intersectedObject.material.color.set(Math.random() * 0xffffff);
                // Optional: remove the object
                // scene.remove(intersectedObject);
                // targets.splice(targets.indexOf(intersectedObject), 1);
            }
        }

        window.addEventListener('click', onMouseClick, false);

        // Animation loop
        function animate() {
            requestAnimationFrame(animate);

            // Rotate targets
            targets.forEach(target => {
                target.rotation.x += 0.01;
                target.rotation.y += 0.01;
            });

            renderer.render(scene, camera);
        }

        animate();
    </script>
</body>
</html>

Claude Code:

We also gave the same prompt to Claude, and it came up with this result.

We can shoot the enemies, which generates confetti, and they disappear. After shooting them all, an alert shows up. It's good, but it still needs a lot of improvement.

Code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D Shooter Game</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            overflow: hidden;
            background: #000;
            font-family: Arial, sans-serif;
            cursor: crosshair;
        }
        
        #gameContainer {
            position: relative;
            width: 100vw;
            height: 100vh;
        }
        
        #ui {
            position: absolute;
            top: 20px;
            left: 20px;
            color: white;
            z-index: 100;
            font-size: 18px;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
        }
        
        #crosshair {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 20px;
            height: 20px;
            border: 2px solid rgba(255, 255, 255, 0.8);
            border-radius: 50%;
            pointer-events: none;
            z-index: 100;
        }
        
        #crosshair::before {
            content: '';
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 6px;
            height: 6px;
            background: rgba(255, 255, 255, 0.8);
            border-radius: 50%;
        }
        
        #instructions {
            position: absolute;
            bottom: 20px;
            left: 20px;
            color: rgba(255, 255, 255, 0.7);
            font-size: 14px;
            z-index: 100;
        }
    </style>
</head>
<body>
    <div id="gameContainer">
        <div id="ui">
            <div>Score: <span id="score">0</span></div>
            <div>Targets: <span id="targets">0</span></div>
        </div>
        <div id="crosshair"></div>
        <div id="instructions">
            Click to shoot targets • Move mouse to aim
        </div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        class ShooterGame {
            constructor() {
                this.scene = null;
                this.camera = null;
                this.renderer = null;
                this.targets = [];
                this.raycaster = new THREE.Raycaster();
                this.mouse = new THREE.Vector2();
                this.score = 0;
                this.targetCount = 0;
                
                this.init();
                this.createTargets();
                this.animate();
                this.bindEvents();
            }
            
            init() {
                // Scene setup
                this.scene = new THREE.Scene();
                this.scene.background = new THREE.Color(0x0a0a0a);
                
                // Camera setup
                this.camera = new THREE.PerspectiveCamera(
                    75, 
                    window.innerWidth / window.innerHeight, 
                    0.1, 
                    1000
                );
                this.camera.position.set(0, 0, 10);
                
                // Renderer setup
                this.renderer = new THREE.WebGLRenderer({ antialias: true });
                this.renderer.setSize(window.innerWidth, window.innerHeight);
                this.renderer.shadowMap.enabled = true;
                this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
                document.getElementById('gameContainer').appendChild(this.renderer.domElement);
                
                // Lighting setup
                this.setupLighting();
                
                // Add ground plane for reference
                this.createGround();
            }
            
            setupLighting() {
                // Ambient light
                const ambientLight = new THREE.AmbientLight(0x404040, 0.3);
                this.scene.add(ambientLight);
                
                // Main directional light
                const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
                directionalLight.position.set(10, 10, 5);
                directionalLight.castShadow = true;
                directionalLight.shadow.mapSize.width = 2048;
                directionalLight.shadow.mapSize.height = 2048;
                this.scene.add(directionalLight);
                
                // Point light for dramatic effect
                const pointLight = new THREE.PointLight(0x00ffff, 0.5, 50);
                pointLight.position.set(-10, 5, 5);
                this.scene.add(pointLight);
                
                // Another point light
                const pointLight2 = new THREE.PointLight(0xff0080, 0.3, 30);
                pointLight2.position.set(10, -5, 8);
                this.scene.add(pointLight2);
            }
            
            createGround() {
                const groundGeometry = new THREE.PlaneGeometry(100, 100);
                const groundMaterial = new THREE.MeshLambertMaterial({ 
                    color: 0x333333,
                    transparent: true,
                    opacity: 0.1
                });
                const ground = new THREE.Mesh(groundGeometry, groundMaterial);
                ground.rotation.x = -Math.PI / 2;
                ground.position.y = -5;
                ground.receiveShadow = true;
                this.scene.add(ground);
            }
            
            createTargets() {
                const targetTypes = ['cube', 'sphere', 'octahedron'];
                const colors = [0xff4444, 0x44ff44, 0x4444ff, 0xffff44, 0xff44ff, 0x44ffff];
                
                for (let i = 0; i < 15; i++) {
                    const targetType = targetTypes[Math.floor(Math.random() * targetTypes.length)];
                    const color = colors[Math.floor(Math.random() * colors.length)];
                    
                    let geometry;
                    switch (targetType) {
                        case 'cube':
                            geometry = new THREE.BoxGeometry(1, 1, 1);
                            break;
                        case 'sphere':
                            geometry = new THREE.SphereGeometry(0.6, 16, 16);
                            break;
                        case 'octahedron':
                            geometry = new THREE.OctahedronGeometry(0.7);
                            break;
                    }
                    
                    const material = new THREE.MeshLambertMaterial({ 
                        color: color,
                        transparent: true,
                        opacity: 0.9
                    });
                    
                    const target = new THREE.Mesh(geometry, material);
                    
                    // Random position
                    target.position.x = (Math.random() - 0.5) * 20;
                    target.position.y = (Math.random() - 0.5) * 10;
                    target.position.z = (Math.random() - 0.5) * 15 - 5;
                    
                    // Random rotation
                    target.rotation.x = Math.random() * Math.PI;
                    target.rotation.y = Math.random() * Math.PI;
                    target.rotation.z = Math.random() * Math.PI;
                    
                    // Add floating animation properties
                    target.userData = {
                        originalY: target.position.y,
                        floatSpeed: 0.5 + Math.random() * 1.5,
                        floatRange: 0.5 + Math.random() * 1,
                        rotationSpeed: {
                            x: (Math.random() - 0.5) * 0.02,
                            y: (Math.random() - 0.5) * 0.02,
                            z: (Math.random() - 0.5) * 0.02
                        }
                    };
                    
                    target.castShadow = true;
                    target.receiveShadow = true;
                    
                    this.targets.push(target);
                    this.scene.add(target);
                }
                
                this.targetCount = this.targets.length;
                this.updateUI();
            }
            
            bindEvents() {
                // Mouse click event
                window.addEventListener('click', (event) => this.shoot(event));
                
                // Window resize event
                window.addEventListener('resize', () => this.onWindowResize());
                
                // Mouse move for potential future features
                window.addEventListener('mousemove', (event) => this.onMouseMove(event));
            }
            
            onMouseMove(event) {
                // Update mouse coordinates for raycasting
                this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
                this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
            }
            
            shoot(event) {
                // Update mouse position
                this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
                this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
                
                // Cast ray from camera through mouse position
                this.raycaster.setFromCamera(this.mouse, this.camera);
                
                // Check for intersections with targets
                const intersects = this.raycaster.intersectObjects(this.targets);
                
                if (intersects.length > 0) {
                    const hitTarget = intersects[0].object;
                    this.hitTarget(hitTarget);
                }
            }
            
            hitTarget(target) {
                // Create explosion effect
                this.createExplosion(target.position);
                
                // Remove target from scene and array
                this.scene.remove(target);
                const index = this.targets.indexOf(target);
                if (index > -1) {
                    this.targets.splice(index, 1);
                }
                
                // Update score
                this.score += 10;
                this.updateUI();
                
                // Check if all targets are destroyed
                if (this.targets.length === 0) {
                    this.gameComplete();
                }
            }
            
            createExplosion(position) {
                const particleCount = 15;
                const particles = [];
                
                for (let i = 0; i < particleCount; i++) {
                    const geometry = new THREE.SphereGeometry(0.05, 4, 4);
                    const material = new THREE.MeshBasicMaterial({ 
                        color: new THREE.Color().setHSL(Math.random(), 1, 0.5),
                        transparent: true,
                        opacity: 1
                    });
                    
                    const particle = new THREE.Mesh(geometry, material);
                    particle.position.copy(position);
                    
                    // Random velocity
                    particle.userData = {
                        velocity: new THREE.Vector3(
                            (Math.random() - 0.5) * 0.3,
                            (Math.random() - 0.5) * 0.3,
                            (Math.random() - 0.5) * 0.3
                        ),
                        life: 60 // frames
                    };
                    
                    particles.push(particle);
                    this.scene.add(particle);
                }
                
                // Animate particles
                const animateParticles = () => {
                    particles.forEach((particle, index) => {
                        particle.position.add(particle.userData.velocity);
                        particle.userData.velocity.multiplyScalar(0.95); // friction
                        particle.userData.life--;
                        particle.material.opacity = particle.userData.life / 60;
                        
                        if (particle.userData.life <= 0) {
                            this.scene.remove(particle);
                            particles.splice(index, 1);
                        }
                    });
                    
                    if (particles.length > 0) {
                        requestAnimationFrame(animateParticles);
                    }
                };
                
                animateParticles();
            }
            
            gameComplete() {
                setTimeout(() => {
                    alert(`Congratulations! All targets destroyed!\nFinal Score: ${this.score}`);
                    this.resetGame();
                }, 500);
            }
            
            resetGame() {
                // Clear existing targets
                this.targets.forEach(target => this.scene.remove(target));
                this.targets = [];
                
                // Reset score
                this.score = 0;
                
                // Create new targets
                this.createTargets();
            }
            
            updateUI() {
                document.getElementById('score').textContent = this.score;
                document.getElementById('targets').textContent = this.targets.length;
            }
            
            onWindowResize() {
                this.camera.aspect = window.innerWidth / window.innerHeight;
                this.camera.updateProjectionMatrix();
                this.renderer.setSize(window.innerWidth, window.innerHeight);
            }
            
            animate() {
                requestAnimationFrame(() => this.animate());
                
                // Animate targets (floating and rotating)
                const time = Date.now() * 0.001;
                
                this.targets.forEach(target => {
                    // Floating animation
                    target.position.y = target.userData.originalY + 
                        Math.sin(time * target.userData.floatSpeed) * target.userData.floatRange;
                    
                    // Rotation animation
                    target.rotation.x += target.userData.rotationSpeed.x;
                    target.rotation.y += target.userData.rotationSpeed.y;
                    target.rotation.z += target.userData.rotationSpeed.z;
                });
                
                this.renderer.render(this.scene, this.camera);
            }
        }
        
        // Initialize the game when the page loads
        window.addEventListener('load', () => {
            new ShooterGame();
        });
    </script>
</body>
</html>

Pricing & Usage

Gemini CLI is free to use with your Google account. You get up to 1,000 requests per day and 60 requests per minute, which is more than enough for most coding work. You can also plug in your own Gemini API key from Google AI Studio if you want more control or need higher limits.

Claude Code is available for Claude Pro and Claude Max users. That means if you already pay for Anthropic's Claude service, you don't need to pay anything extra, Claude Code is included. There's no free tier, so if you're not a subscriber, you won't be able to use it.

So in short:

ToolFree TierPaid RequirementNotes
Gemini CLI✅ 1,000/day❌ Not requiredOptional API key for custom setups
Claude Code❌ None✅ Claude Pro/Max onlyComes bundled, but no access without plan

Conclusion

Both Gemini CLI and Claude Code are helpful in their own ways.

Gemini CLI is better if you just want to fix something quickly, run a few commands, or get short answers without much setup. It stays out of your way and works well for small tasks or command-line routines.

Claude Code, on the other hand, is more thoughtful. It handles bigger tasks, understands your code better, and even helps with design, Git, and building things from scratch. It feels more like a coding partner than a tool.

In the end, it depends on how you work. If you like to move fast with simple prompts, Gemini is solid. But if you're doing deeper work and want better support across the board, Claude Code gives you more to work with.

Links:

hero

Streamline your Engineering Team

Get started with a Free Trial or Book A Demo with the founder
footer
logo

Building artificial
engineering intelligence.

Product

Home

Log In

Sign Up

Helpful Links

OSS Explore

PR Arena

IDE

Resources

Blog

Changelog

Startups

Contact Us

Careers