CYBER DETECTIVE

Inspiration

Inspired by the critical, high-pressure role of subreddit moderation, CYBER DETECTIVE bridges the gap between community management and immersive gameplay. We were fascinated by the challenge of filtering truth from noise in vibrant, fast-moving digital communities like Reddit. Our goal was to simulate this challenge, drawing heavy aesthetic inspiration from the Matrix, cyberpunk culture, and the "digital rain" of retro-hacking. We built this effect ourselves using the HTML5 Canvas API, ensuring a lightweight, thematic backdrop for the entire experience. We envisioned a tool that felt both like a game and a professional moderation dashboard, using the gamification possibilities of the Kiro Panel as our design blueprint.

// src/components/matrixbackground.jsx
// ...
const draw = () => {
  ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = '#33ff33';
  ctx.font = `${fontSize}px 'VT323', monospace`;

  for (let i = 0; i < drops.length; i++) {
    const text = characters[Math.floor(Math.random() * characters.length)];
    ctx.fillText(text, i * fontSize, drops[i] * fontSize);
    if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) {
      drops[i] = 0;
    }
    drops[i]++;
  }
  animationFrameId = requestAnimationFrame(draw);
};
// ...

What it does

CYBER DETECTIVE places the player in the role of an "Operator" tasked with "securing" compromised data nodes. These nodes are presented as Reddit-style comment threads populated with different data fragments.

The Player Experience Flow

  1. Boot Up: The player is greeted with a <LoadingScreen> and a cinematic <IntroScene> built with Phaser.js.
  2. Login: The player must "jack in" by entering an "Operator Alias" in the <UsernameInput> component.
  3. Console: They land at the <MainMenu>, which acts as the central hub to start the game, view the <Leaderboard>, or access <Settings>.
  4. Briefing: Before each case, a TypeAnimation component delivers a narrative briefing from the "Handler," setting the stage for the mission.
  5. Investigation: The player enters the core gameplay loop within the <InvestigationThread> component.

The Core Gameplay Loop

The player must:

  • Analyze & Decrypt: Sift through a feed of data fragments. Some fragments are [CORRUPTED] and must be decrypted. Players can either run a 3-second manual "Analyze"—which grants a +15 score bonus—or use their instant "Overclock" ability.
  • Vote & Filter: Just like a moderator, the player must assess each fragment's type: is it a real lead, useless noise, or a deceptive agent trap?. They use upvote/downvote buttons to make their choice. Correct votes build a scoreMultiplier (up to 3x), but mistakes are costly. For example, upvoting an agent incurs a -100 score penalty and drains 15 seconds from the clock.
  • Link & Solve: The ultimate goal is to find the hidden connections. The player must select two upvoted, decrypted leads and attempt to "Link" them. A correct link reveals a piece of the puzzle and grants a large +250 score bonus. Finding all correctLinks for a case secures the node.
  • Manage Resources: All this is done under the pressure of a ticking timer. Players must manage their score (which is a resource for the "Intel" hint ability) and their ability cooldowns (Overclock, Noise Filter) to solve the case before time runs out.
// src/App.jsx - Example of a penalty
if (voteType === 'up') {
  if (fragmentType === 'lead') {
       scoreChange = 50 * scoreMultiplier; correctAction = true;
  } else if (fragmentType === 'agent') {
    scoreChange = -100;
    setTimeLeft(prevTime => Math.max(0, prevTime - 15)); // Time penalty
    feedbackType = 'agent_detected_shake';
    feedbackMsg = 'TRACE DETECTED! AGENT SIGNATURE.';
    correctAction = false;
  } 
  // ...
}

Technical Architecture: How we built it

The game is a Single-Page Application (SPA) that uniquely blends two frameworks: a Phaser.js IntroScene provides a cinematic, hardware-accelerated opening with tweens and music, before handing off to the main React.js application.

  • Centralized State Management (React Hooks): The core App.jsx component serves as the "brain" of the game. We built the entire application without Redux or other state libraries, relying purely on React Hooks.
    • useState: Manages all game state, including playerStats, votes, resolvedLinks, decryptedData, currentCaseIndex, and lastLinkFeedback.
    • useRef: Crucial for managing the master game timer. timerIntervalRef holds a stable reference to the setInterval ID, allowing us to accurately start, stop, and clear the timer across re-renders.
    • useEffect: Manages all time-based game loops, including the main timeLeft timer and the abilityCooldowns countdown.
// src/App.jsx - Centralized State
const AppContent = () => {
  // ...
  const [playerStats, setPlayerStats] = useState({ score: 0, falseLinks: 0 });
  const [currentCaseIndex, setCurrentCaseIndex] = useState(0);
  const [votes, setVotes] = useState({});
  const [resolvedLinks, setResolvedLinks] = useState({});
  const [decryptedData, setDecryptedData] = useState({});
  const [lastLinkFeedback, setLastLinkFeedback] = useState(null);
  const [timeLeft, setTimeLeft] = useState(cases[0]?.timeLimit || 90);
  const timerIntervalRef = useRef(null);
  const [abilityCooldowns, setAbilityCooldowns] = useState({ overclock: 0, filter: 0 });
  // ...
}
  • Component Structure (Prop Drilling): Logic is passed down from App.jsx.
1.  **App.jsx** holds all logic (`handleVote`, `handleLinkAttempt`) and state.
2.  It passes these down as props to **`<InvestigationThread />`**, which handles the layout and UI.
3.  **`<InvestigationThread />`** then passes the specific handlers (like `onVote`, `activateOverclock`) and data down to each individual **`<FragmentComment />`** component.
  • Persistent Data (localStorage): We used the browser's localStorage API to provide data persistence. This saves the player's Top 10 "Operator Rankings" (under the key cyberDetectiveLeaderboard_v2) and their visual setting preferences (under cyberDetectiveSettings_v1) between sessions.
// src/App.jsx - Local Storage for Leaderboard
const LEADERBOARD_KEY = 'cyberDetectiveLeaderboard_v2';
const loadLeaderboard = () => {
    try {
        const stored = localStorage.getItem(LEADERBOARD_KEY);
        return stored ? JSON.parse(stored) : defaultLeaderboard;
    } catch (error) {
        console.error("Failed to load leaderboard from localStorage:", error);
        return defaultLeaderboard;
    }
};
  • Styling (Pure CSS): The entire retro-hacking aesthetic is achieved with pure CSS. We used a consistent class-based system (neon-frame, glow-text), CSS animations for progress bars and feedback, and a scanlines overlay to create an immersive, non-library-dependent visual experience.

Challenges we ran into

  • Algorithmic Balancing: Our biggest challenge was tuning the risk-vs-reward. We had to carefully balance the score/time penalties for upvoting an agent (a -100 score and -15 second penalty) against the high reward for a correctLink (+250 score). This required iterating on the cases data structure in App.jsx to ensure a fair but challenging difficulty curve.
  • Complex State Propagation: We needed a way for a single action in App.jsx to trigger multiple, separate UI components. We solved this by using the lastLinkFeedback state object as a "state-based event bus." When handleLinkAttempt is called, it sets this object. useEffect hooks in App.jsx and prop-drilling to InvestigationThread.jsx watch this object, which in turn triggers the <FeedbackPanel> message, the CSS screen-shake, and the flashing effect on fragments.
  • Timer & Interval Management: Managing JavaScript setInterval within React's lifecycle is notoriously difficult. We ran into bugs with timers accelerating or not clearing. The solution was to store the interval ID in a useRef (timerIntervalRef), which is stable across re-renders and can be reliably accessed from useEffect cleanup functions.
// src/App.jsx - Example of state cascade on a correct link
if (correctLink) {
    const scoreChange = 250 * scoreMultiplier;
    setPlayerStats(prev => ({ ...prev, score: prev.score + scoreChange })); // 1. Update score
    addScorePopup(id2, scoreChange); // 2. Trigger score popup
    setResolvedLinks(prev => ({ ...prev, [correctLink.id]: true })); // 3. Update link state
    setLastLinkFeedback({ type: 'correct', message: correctLink.explanation, ids: sortedIds }); // 4. Trigger feedback panel
    setScoreMultiplier(prev => Math.min(3, prev + 1)); // 5. Update multiplier
    // ... logic to check for case complete ...
}

Accomplishments that we're proud of

  • A Complete End-to-End Game Loop: We successfully built a fully playable experience, from the initial UsernameInput screen, through multiple InvestigationThread cases with progression, to a persistent Leaderboard finale.
  • A Robust, Multi-Layered Mechanic: The core gameplay isn't just one mechanic, but four interconnected systems: the Decryption (with its 3-second progress bar), the Voting (with its scoreMultiplier), the Linking (with its validation logic), and the Ability system (with cooldowns).
  • Fully Operational Moderator Abilities: We implemented three distinct, strategic abilities—Overclock (60s cooldown), Noise Filter (90s cooldown), and Intel (?) (costs 50 score)—all managed by a single abilityCooldowns state object and a setInterval in App.jsx, adding a layer of player choice and resource management.
  • Multi-Channel Immersive Feedback: We are especially proud of our feedback system. A single player action, from a simple vote to a case-completing link, provides immediate visual (<ScorePopup /> animation), textual (<FeedbackPanel />), and environmental (screen-shake) feedback, which keeps the player fully engaged.
  • Product Polish: Beyond the core loop, we implemented a complete, polished user experience, including a <Settings> panel for user choice, a <LoadingScreen>, and clear, animated overlays for case briefings and completion.
// src/App.jsx - Ability Cooldown Logic
useEffect(() => {
  const cooldownInterval = setInterval(() => {
    setAbilityCooldowns(prev => ({
      overclock: Math.max(0, prev.overclock - 1),
      filter: Math.max(0, prev.filter - 1),
    }));
  }, 1000);
  return () => clearInterval(cooldownInterval);
}, []);

What we learned

  • Advanced React State & Effect Management: This project forced us to master complex React patterns. We learned how to manage all game logic purely with Hooks, using useEffect for all time-based logic and useRef to maintain stable timer intervals.
  • Gamifying Abstract Concepts: We learned how to translate the abstract challenge of "community moderation" into concrete, measurable game mechanics. Concepts like "discerning intent" became the act of sorting leads, noise, and agents, each with a clear, calculated consequence.
  • Building a Professional Dashboard UI: We learned how to use React to build a clean, panel-based dashboard that feels like a professional tool. This involved modular component design and a centralized state (App.jsx) to keep all panels synchronized.

What's next for CYBER DETECTIVE

  • Multiplayer Investigation Modes: The next logical step is collaborative moderation. This would involve using a backend (like Firebase or Socket.io) to synchronize the App.jsx game state across multiple players, allowing teams to solve a case together.
  • Deeper Kiro Integration: Leverage Kiro's panel architecture more directly for AI Agent logic. We envision "Agents" as dynamic entities that respond to moderation actions (like downvotes) in real-time within a Kiro-managed dashboard.
  • Scalable Case Content: Our cases array structure is built for expansion. We plan to add dozens of new cases with more complex correctLinks, branching narratives, and fragments that are harder to distinguish.
  • Expanded Moderator Toolkit: We can easily add new abilities to the existing abilityCooldowns state. Future ideas include "Quarantine" (to pause the timer at the cost of score) or "Shadowban" (to permanently hide all fragments from a specific noise user).

Built With

Share this project:

Updates