Google Antigravity Directory

The #1 directory for Google Antigravity prompts, rules, workflows & MCP servers. Optimized for Gemini 3 agentic development.

Resources

PromptsMCP ServersAntigravity RulesGEMINI.md GuideBest Practices

Company

Submit PromptAntigravityAI.directory

Popular Prompts

Next.js 14 App RouterReact TypeScriptTypeScript AdvancedFastAPI GuideDocker Best Practices

Legal

Privacy PolicyTerms of ServiceContact Us
Featured on FazierFeatured on WayfindioAntigravity AI - Featured on Startup FameFeatured on Wired BusinessFeatured on Twelve ToolsListed on Turbo0Featured on findly.toolsFeatured on Aura++That App ShowAI ToolzShinyLaunchMillion Dot HomepageSolver ToolsFeatured on FazierFeatured on WayfindioAntigravity AI - Featured on Startup FameFeatured on Wired BusinessFeatured on Twelve ToolsListed on Turbo0Featured on findly.toolsFeatured on Aura++That App ShowAI ToolzShinyLaunchMillion Dot HomepageSolver Tools

© 2026 Antigravity AI Directory. All rights reserved.

The #1 directory for Google Antigravity IDE

This website is not affiliated with, endorsed by, or associated with Google LLC. "Google" and "Gemini" are trademarks of Google LLC.

Antigravity AI Directory
PromptsMCPBest PracticesUse CasesLearn
Home
Prompts
PartyKit Realtime Collaboration

PartyKit Realtime Collaboration

Master PartyKit realtime collaboration patterns for Google Antigravity IDE multiplayer applications

PartyKitRealtimeCollaborationTypeScript
by Antigravity AI
⭐0Stars
.antigravity
# PartyKit Realtime Collaboration for Google Antigravity IDE

Build multiplayer experiences with PartyKit using Google Antigravity IDE. This comprehensive guide covers party servers, real-time sync, presence awareness, and collaborative editing patterns.

## Party Server Setup

```typescript
// party/main.ts
import type { Party, Connection, ConnectionContext } from "partykit/server";

interface UserInfo {
  id: string;
  name: string;
  color: string;
  cursor?: { x: number; y: number };
}

interface RoomState {
  users: Map<string, UserInfo>;
  document: {
    content: string;
    version: number;
  };
  cursors: Map<string, { x: number; y: number }>;
}

type Message =
  | { type: "join"; user: UserInfo }
  | { type: "leave"; userId: string }
  | { type: "cursor"; userId: string; position: { x: number; y: number } }
  | { type: "edit"; operation: Operation; version: number }
  | { type: "sync"; state: Partial<RoomState> };

interface Operation {
  type: "insert" | "delete";
  position: number;
  text?: string;
  length?: number;
}

export default class CollaborationRoom implements Party {
  private state: RoomState = {
    users: new Map(),
    document: { content: "", version: 0 },
    cursors: new Map(),
  };

  constructor(public party: Party) {}

  async onStart() {
    const stored = await this.party.storage.get<RoomState>("state");
    if (stored) {
      this.state = {
        ...stored,
        users: new Map(Object.entries(stored.users || {})),
        cursors: new Map(Object.entries(stored.cursors || {})),
      };
    }
  }

  async onConnect(connection: Connection, ctx: ConnectionContext) {
    const url = new URL(ctx.request.url);
    const userInfo: UserInfo = {
      id: connection.id,
      name: url.searchParams.get("name") || "Anonymous",
      color: this.generateColor(),
    };

    this.state.users.set(connection.id, userInfo);

    connection.send(
      JSON.stringify({
        type: "sync",
        state: {
          users: Object.fromEntries(this.state.users),
          document: this.state.document,
          cursors: Object.fromEntries(this.state.cursors),
        },
      })
    );

    this.broadcast(
      JSON.stringify({ type: "join", user: userInfo }),
      [connection.id]
    );
  }

  async onMessage(message: string, connection: Connection) {
    const data = JSON.parse(message) as Message;

    switch (data.type) {
      case "cursor":
        this.state.cursors.set(data.userId, data.position);
        this.broadcast(message, [connection.id]);
        break;

      case "edit":
        if (data.version === this.state.document.version) {
          this.applyOperation(data.operation);
          this.state.document.version++;
          this.broadcast(
            JSON.stringify({
              type: "edit",
              operation: data.operation,
              version: this.state.document.version,
            })
          );
          await this.party.storage.put("state", this.state);
        } else {
          connection.send(
            JSON.stringify({
              type: "sync",
              state: { document: this.state.document },
            })
          );
        }
        break;
    }
  }

  async onClose(connection: Connection) {
    const user = this.state.users.get(connection.id);
    this.state.users.delete(connection.id);
    this.state.cursors.delete(connection.id);

    if (user) {
      this.broadcast(
        JSON.stringify({ type: "leave", userId: connection.id })
      );
    }
  }

  private applyOperation(operation: Operation) {
    const doc = this.state.document;
    if (operation.type === "insert" && operation.text) {
      doc.content =
        doc.content.slice(0, operation.position) +
        operation.text +
        doc.content.slice(operation.position);
    } else if (operation.type === "delete" && operation.length) {
      doc.content =
        doc.content.slice(0, operation.position) +
        doc.content.slice(operation.position + operation.length);
    }
  }

  private broadcast(message: string, exclude: string[] = []) {
    for (const connection of this.party.getConnections()) {
      if (!exclude.includes(connection.id)) {
        connection.send(message);
      }
    }
  }

  private generateColor(): string {
    const colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEAA7"];
    return colors[Math.floor(Math.random() * colors.length)];
  }
}
```

## React Client Hook

```typescript
// src/hooks/usePartyKit.ts
import usePartySocket from "partysocket/react";
import { useState, useCallback } from "react";

interface UseCollaborationOptions {
  roomId: string;
  userName: string;
}

export function useCollaboration({ roomId, userName }: UseCollaborationOptions) {
  const [users, setUsers] = useState<Map<string, UserInfo>>(new Map());
  const [document, setDocument] = useState({ content: "", version: 0 });
  const [cursors, setCursors] = useState<Map<string, { x: number; y: number }>>(new Map());
  const [isConnected, setIsConnected] = useState(false);

  const socket = usePartySocket({
    host: process.env.NEXT_PUBLIC_PARTYKIT_HOST!,
    room: roomId,
    query: { name: userName },
    onOpen() {
      setIsConnected(true);
    },
    onClose() {
      setIsConnected(false);
    },
    onMessage(event) {
      const message = JSON.parse(event.data);

      switch (message.type) {
        case "sync":
          if (message.state.users) {
            setUsers(new Map(Object.entries(message.state.users)));
          }
          if (message.state.document) {
            setDocument(message.state.document);
          }
          break;

        case "join":
          setUsers((prev) => new Map(prev).set(message.user.id, message.user));
          break;

        case "leave":
          setUsers((prev) => {
            const next = new Map(prev);
            next.delete(message.userId);
            return next;
          });
          break;

        case "cursor":
          setCursors((prev) => new Map(prev).set(message.userId, message.position));
          break;
      }
    },
  });

  const sendCursor = useCallback(
    (position: { x: number; y: number }) => {
      socket.send(JSON.stringify({ type: "cursor", userId: socket.id, position }));
    },
    [socket]
  );

  const sendEdit = useCallback(
    (operation: Operation) => {
      socket.send(JSON.stringify({ type: "edit", operation, version: document.version }));
    },
    [socket, document.version]
  );

  return { users: Array.from(users.values()), document, cursors, isConnected, sendCursor, sendEdit, myId: socket.id };
}
```

## Collaborative Editor Component

```typescript
// src/components/CollaborativeEditor.tsx
"use client";

import { useRef, useEffect } from "react";
import { useCollaboration } from "@/hooks/usePartyKit";

export function CollaborativeEditor({ roomId }: { roomId: string }) {
  const editorRef = useRef<HTMLTextAreaElement>(null);
  const { users, document, cursors, isConnected, sendCursor, sendEdit, myId } = useCollaboration({ roomId, userName: "User" });

  useEffect(() => {
    const handleMouseMove = (e: MouseEvent) => {
      const rect = editorRef.current?.getBoundingClientRect();
      if (rect) {
        sendCursor({ x: e.clientX - rect.left, y: e.clientY - rect.top });
      }
    };

    window.addEventListener("mousemove", handleMouseMove);
    return () => window.removeEventListener("mousemove", handleMouseMove);
  }, [sendCursor]);

  const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    const newValue = e.target.value;
    const oldValue = document.content;
    
    if (newValue.length > oldValue.length) {
      const position = e.target.selectionStart - 1;
      const text = newValue.slice(position, position + (newValue.length - oldValue.length));
      sendEdit({ type: "insert", position, text });
    } else {
      const position = e.target.selectionStart;
      const length = oldValue.length - newValue.length;
      sendEdit({ type: "delete", position, length });
    }
  };

  return (
    <div className="collaborative-editor">
      <div className="presence-bar">
        {users.map((user) => (
          <div key={user.id} className="user-avatar" style={{ backgroundColor: user.color }} title={user.name}>
            {user.name[0]}
          </div>
        ))}
        <span className={isConnected ? "connected" : "disconnected"}>
          {isConnected ? "Connected" : "Reconnecting..."}
        </span>
      </div>

      <div className="editor-container">
        <textarea ref={editorRef} value={document.content} onChange={handleChange} className="editor" />

        {Array.from(cursors.entries()).map(([userId, pos]) => {
          if (userId === myId) return null;
          const user = users.find((u) => u.id === userId);
          return (
            <div key={userId} className="cursor" style={{ left: pos.x, top: pos.y, borderColor: user?.color }}>
              <span className="cursor-label" style={{ backgroundColor: user?.color }}>{user?.name}</span>
            </div>
          );
        })}
      </div>
    </div>
  );
}
```

## Best Practices for Google Antigravity IDE

When using PartyKit with Google Antigravity, design party servers for specific collaboration patterns. Implement conflict resolution for concurrent edits. Use presence for user awareness. Persist important state to storage. Handle reconnection gracefully. Let Gemini 3 generate party server logic from collaboration requirements.

Google Antigravity excels at building real-time multiplayer features with PartyKit.

When to Use This Prompt

This PartyKit prompt is ideal for developers working on:

  • PartyKit applications requiring modern best practices and optimal performance
  • Projects that need production-ready PartyKit code with proper error handling
  • Teams looking to standardize their partykit development workflow
  • Developers wanting to learn industry-standard PartyKit patterns and techniques

By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their partykit implementations.

How to Use

  1. Copy the prompt - Click the copy button above to copy the entire prompt to your clipboard
  2. Paste into your AI assistant - Use with Claude, ChatGPT, Cursor, or any AI coding tool
  3. Customize as needed - Adjust the prompt based on your specific requirements
  4. Review the output - Always review generated code for security and correctness
💡 Pro Tip: For best results, provide context about your project structure and any specific constraints or preferences you have.

Best Practices

  • ✓ Always review generated code for security vulnerabilities before deploying
  • ✓ Test the PartyKit code in a development environment first
  • ✓ Customize the prompt output to match your project's coding standards
  • ✓ Keep your AI assistant's context window in mind for complex requirements
  • ✓ Version control your prompts alongside your code for reproducibility

Frequently Asked Questions

Can I use this PartyKit prompt commercially?

Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.

Which AI assistants work best with this prompt?

This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.

How do I customize this prompt for my specific needs?

You can modify the prompt by adding specific requirements, constraints, or preferences. For PartyKit projects, consider mentioning your framework version, coding style, and any specific libraries you're using.

Related Prompts

💬 Comments

Loading comments...