Git City 3d Github Visualization
Build and extend Git City — a 3D pixel art city where GitHub profiles become interactive buildings using Next.js, Three.js, and Supabase.
- Skill ID
- reason-machines/trending-skills/git-city-3d-github-visualization
- Publisher
- reason-machines
- Repository
- trending-skills
- Installs
- 0
- Files
- 1
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
reason-machines/trending-skills/git-city-3d-github-visualizationInstalls these files- SKILL.md
What this skill tells the agent
Git City — 3D GitHub Profile Visualization
Skill by ara.so — Daily 2026 Skills collection.
Git City transforms GitHub profiles into a 3D pixel art city. Each user becomes a unique building: height from contributions, width from repos, window brightness from stars. Built with Next.js 16 (App Router), React Three Fiber, and Supabase.
Quick Setup
git clone https://github.com/srizzon/git-city.git
cd git-city
npm install
# Copy env template
cp .env.example .env.local # Linux/macOS
copy .env.example .env.local # Windows CMD
Copy-Item .env.example .env.local # PowerShell
npm run dev
# → http://localhost:3001Environment Variables
Fill in .env.local after copying:
# Supabase — Project Settings → API
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
# GitHub — Settings → Developer settings → Personal access tokens
GITHUB_TOKEN=github_pat_your_token_here
# Optional: comma-separated GitHub logins for /admin/ads access
ADMIN_GITHUB_LOGINS=your_github_loginFinding Supabase values: Dashboard → Project Settings → API Finding GitHub token: github.com → Settings → Developer settings → Personal access tokens (fine-grained recommended)
Project Structure
git-city/
├── app/ # Next.js App Router pages
│ ├── page.tsx # Main city view
│ ├── [username]/ # User profile pages
│ ├── compare/ # Side-by-side compare mode
│ └── admin/ # Admin panel
├── components/
│ ├── city/ # 3D city scene components
│ │ ├── Building.tsx # Individual building mesh
│ │ ├── CityScene.tsx # Main R3F canvas/scene
│ │ └── LODManager.tsx # Level-of-detail system
│ ├── ui/ # 2D overlay UI components
│ └── profile/ # Profile page components
├── lib/
│ ├── github.ts # GitHub API helpers
│ ├── supabase/ # Supabase client + server utils
│ ├── buildings.ts # Building metric calculations
│ └── achievements.ts # Achievement logic
├── hooks/ # Custom React hooks
├── types/ # TypeScript type definitions
└── public/ # Static assetsCore Concepts
Building Metrics Mapping
Buildings are generated from GitHub profile data:
// lib/buildings.ts pattern
interface BuildingMetrics {
height: number; // Based on total contributions
width: number; // Based on public repo count
windowBrightness: number; // Based on total stars received
windowPattern: number[]; // Based on recent activity pattern
}
function calculateBuildingMetrics(profile: GitHubProfile): BuildingMetrics {
const height = Math.log10(profile.totalContributions + 1) * 10;
const width = Math.min(Math.ceil(profile.publicRepos / 10), 8);
const windowBrightness = Math.min(profile.totalStars / 1000, 1);
return { height, width, windowBrightness, windowPattern: [] };
}3D Building Component (React Three Fiber)
// components/city/Building.tsx pattern
import { useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three';
interface BuildingProps {
position: [number, number, number];
metrics: BuildingMetrics;
username: string;
isSelected?: boolean;
onClick?: () => void;
}
export function Building({ position, metrics, username, isSelected, onClick }: BuildingProps) {
const meshRef = useRef<THREE.Mesh>(null);
// Animate selected building
useFrame((state) => {
if (meshRef.current && isSelected) {
meshRef.current.rotation.y = Math.sin(state.clock.elapsedTime) * 0.05;
}
});
return (
<group position={position} onClick={onClick}>
{/* Main building body */}
<mesh ref={meshRef}>
<boxGeometry args={[metrics.width, metrics.height, metrics.width]} />
<meshStandardMaterial color="#1a1a2e" />
</mesh>
{/* Windows as instanced meshes for performance */}
<WindowInstances metrics={metrics} />
</group>
);
}Instanced Meshes for Performance
Git City uses instanced rendering for windows — critical for a city with many buildings:
// components/city/WindowInstances.tsx pattern
import { useRef, useEffect } from 'react';
import { InstancedMesh, Matrix4, Color } from 'three';
export function WindowInstances({ metrics }: { metrics: BuildingMetrics }) {
const meshRef = useRef<InstancedMesh>(null);
useEffect(() => {
if (!meshRef.current) return;
const matrix = new Matrix4();
const color = new Color();
let index = 0;
// Calculate window positions based on building dimensions
for (let floor = 0; floor < metrics.height; floor++) {
for (let col = 0; col < metrics.width; col++) {
const isLit = metrics.windowPattern[index] > 0.5;
matrix.setPosition(col * 1.1 - metrics.width / 2, floor * 1.2, 0.51);
meshRef.current.setMatrixAt(index, matrix);
meshRef.current.setColorAt(
index,
color.set(isLit ? '#FFD700' : '#1a1a2e')
);
index++;
}
}
meshRef.current.instanceMatrix.needsUpdate = true;
if (meshRef.current.instanceColor) {
meshRef.current.instanceColor.needsUpdate = true;
}
}, [metrics]);
const windowCount = Math.floor(metrics.height) * metrics.width;
return (
<instancedMesh ref={meshRef} args={[undefined, undefined, windowCount]}>
<planeGeometry args={[0.4, 0.5]} />
<meshBasicMaterial />
</instancedMesh>
);
}GitHub API Integration
// lib/github.ts pattern
import { Octokit } from '@octokit/rest';
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
export async function fetchGitHubProfile(username: string) {
const [userResponse, reposResponse] = await Promise.all([
octokit.users.getByUsername({ username }),
octokit.repos.listForUser({ username, per_page: 100, sort: 'updated' }),
]);
const totalStars = reposResponse.data.reduce(
(sum, repo) => sum + (repo.stargazers_count ?? 0),
0
);
return {
username: userResponse.data.login,
avatarUrl: userResponse.data.avatar_url,
publicRepos: userResponse.data.public_repos,
followers: userResponse.data.followers,
totalStars,
};
}
export async function fetchContributionData(username: string): Promise<number> {
// Use GitHub GraphQL for contribution calendar data
const query = `
query($username: String!) {
user(login: $username) {
contributionsCollection {
contributionCalendar {
totalContributions
weeks {
contributionDays {
contributionCount
date
}
}
}
}
}
}
`;
const response = await fetch('https://api.github.com/graphql', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ query, variables: { username } }),
});
const data = await response.json();
return data.data.user.contributionsCollection.contributionCalendar.totalContributions;
}Supabase Integration
// lib/supabase/server.ts pattern — server-side client
import { createServerClient } from '@supabase/ssr';
import { cookies } from 'next/headers';
export function createClient() {
const cookieStore = cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll: () => cookieStore.getAll(),
setAll: (cookiesToSet) => {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
);
},
},
}
);
}
More from reason-machines
Use this skill in a project
Open any RiverX project, search the Skills panel for reason-machines/trending-skills/git-city-3d-github-visualization, and install it in one click.
