PitchVibe is an early-stage venture discovery platform engineered to give founders a frictionless stage to pitch ideas, receive automated AI viability critiques powered by Google Gemini, and secure early community traction through upvotes and investor visibility. Architected with Next.js 15, React 19, TypeScript, and Sanity Headless CMS, the application features a 3-step progressive authoring wizard with slash-command markdown editing, category-based discovery (SaaS, FinTech, HealthTech, AI), and zero-latency optimistic upvoting.
The traditional angel fundraising process is riddled with gatekeeping, sluggish pitch deck reviews, and opaque feedback loops. PitchVibe democratizes startup visibility by providing an open, high-velocity discovery ecosystem. Founders can broadcast concise problem-solution narratives, receive instant algorithmic critique on product-market viability from Google Gemini AI, and let the broader tech community validate ideas through upvotes and view metrics.
Every pitch submitted to PitchVibe can trigger an automated multi-vector analysis evaluated against venture criteria:
Server-side integration enforces JSON schemas and evaluates startup submissions across four venture criteria:
// src/app/api/analyze-pitch/route.ts - Google Gemini AI Pitch Viability Evaluator
import { GoogleGenerativeAI } from "@google/generative-ai";
import { NextResponse } from "next/server";
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!);
interface PitchEvaluationPayload {
title: string;
category: "SaaS" | "FinTech" | "AI / Tech" | "Health / Wellness" | "E-Commerce";
shortDescription: string;
fullMarkdownPitch: string;
}
export async function POST(req: Request) {
try {
const payload: PitchEvaluationPayload = await req.json();
const model = genAI.getGenerativeModel({
model: "gemini-1.5-flash",
generationConfig: {
responseMimeType: "application/json",
temperature: 0.2, // Low temperature for consistent, analytical scoring
},
});
const prompt = `
You are an expert Silicon Valley seed-stage venture investor and startup advisor.
Analyze the following startup pitch and return a valid JSON object matching this schema:
{
"viabilityScore": number (0-100),
"executiveSummary": string,
"strengths": string[],
"potentialRisks": string[],
"recommendedActionItems": string[]
}
Startup Title: ${payload.title}
Category: ${payload.category}
Elevator Pitch: ${payload.shortDescription}
Full Pitch Details:
${payload.fullMarkdownPitch}
`;
const result = await model.generateContent(prompt);
const evaluation = JSON.parse(result.response.text());
return NextResponse.json({ success: true, evaluation });
} catch (error) {
console.error("Gemini AI Analysis Error:", error);
return NextResponse.json(
{ success: false, error: "Failed to evaluate startup pitch." },
{ status: 500 }
);
}
}A compelling startup pitch requires both brevity and depth. PitchVibe guides founders through an intuitive 3-step progressive authoring wizard: Step 1 collects core identity (title with 0/80 character limit, industry taxonomy, punchy 0/200 description, and cover image), Step 2 provides a Notion-style markdown editor with slash ('/') commands, and Step 3 handles instant publication to the Sanity Content Lake.
To minimize friction while enforcing data cleanliness before database commits:
Server Action validates payload constraints via Zod before committing the new startup document to Sanity:
// src/app/actions/submitPitch.ts - Validated Sanity Startup Mutation
"use server";
import { writeClient } from "@/lib/sanity.client";
import { revalidatePath } from "next/cache";
import { z } from "zod";
const pitchSchema = z.object({
title: z.string().min(5).max(80),
category: z.enum(["AI / Tech", "E-Commerce", "Health / Wellness", "SaaS", "FinTech"]),
shortDescription: z.string().min(20).max(200),
coverImage: z.string().url(),
pitchMarkdown: z.string().min(50),
authorId: z.string(),
});
export async function submitStartupPitch(formData: FormData) {
const rawData = {
title: formData.get("title") as string,
category: formData.get("category") as string,
shortDescription: formData.get("shortDescription") as string,
coverImage: formData.get("coverImage") as string,
pitchMarkdown: formData.get("pitchMarkdown") as string,
authorId: formData.get("authorId") as string,
};
const parsed = pitchSchema.parse(rawData);
// Commit document to Sanity Content Lake
const newPitch = await writeClient.create({
_type: "startup",
title: parsed.title,
slug: {
_type: "slug",
current: parsed.title.toLowerCase().replace(/[^a-z0-9]+/g, "-"),
},
category: parsed.category,
description: parsed.shortDescription,
image: parsed.coverImage,
pitch: parsed.pitchMarkdown,
author: { _type: "reference", _ref: parsed.authorId },
views: 0,
upvotes: 0,
createdAt: new Date().toISOString(),
});
revalidatePath("/startup");
revalidatePath("/");
return { success: true, slug: newPitch.slug.current };
}A platform is only as useful as its signal-to-noise ratio. PitchVibe features an algorithmic discovery feed with dynamic sorting ('Recent', 'Most Upvoted', 'Most Viewed') and real-time category filtering. Upvotes are updated optimistically in the client DOM while atomic mutations prevent voter replay attacks.
Voting integrity is maintained through multi-layered edge protection:
Atomic patch execution ensures consistency across concurrent investor votes:
// src/app/actions/vote.ts - Atomic Upvote Handler with Anti-Spam Guard
"use server";
import { writeClient } from "@/lib/sanity.client";
import { revalidateTag } from "next/cache";
export async function upvoteStartup(startupId: string, voterId: string) {
try {
// 1. Verify voter hasn't already voted in current cycle (Stored in KV or user votes array)
const alreadyVoted = await checkExistingVote(startupId, voterId);
if (alreadyVoted) {
return { success: false, message: "You have already upvoted this pitch." };
}
// 2. Atomic increment on Sanity Document
await writeClient
.patch(startupId)
.inc({ upvotes: 1 })
.append("voters", [voterId])
.commit();
// 3. Invalidate specific startup ISR cache tag
revalidateTag(`startup-${startupId}`);
return { success: true };
} catch (err) {
console.error("Upvote mutation failed:", err);
return { success: false, error: "Failed to record upvote." };
}
}Instead of managing complex database migrations, schema drifts, and custom asset CDNs, I architected PitchVibe's backend on Sanity.io. Using GROQ (Graph-Relational Object Queries), the frontend requests precisely the data shape needed for feed cards—eliminating payload bloat and providing instant global CDN caching.
| Architectural Layer | PitchVibe (Sanity + Next.js 15) | Traditional Relational Architecture |
|---|---|---|
| Data Querying | Precise GROQ projection filters only title, category, author avatar, and upvotes | Over-fetching SQL SELECT queries requiring manual joins and DTO mappers |
| Asset Processing | Sanity Image Pipeline with automated WebP format negotiation & crop coordinates | Self-managed S3 bucket, Sharp image processing workers, and CDN invalidate queues |
| Schema Evolution | Code-as-schema JavaScript definitions with zero migration downtime | Rigid SQL schema migrations requiring maintenance windows and rollback scripts |
| Global Edge Caching | On-demand Next.js tag-based ISR revalidated via Sanity webhooks in <150ms | Manual Redis cache invalidation keys prone to stale state bugs across pods |
A fast website converts visitors into active platform participants. PitchVibe utilizes Next.js 15 Partial Prerendering (PPR), Turbopack compilation, and modern CSS variables to achieve near-perfect Lighthouse scores across desktop and mobile devices.
| Technical Dimension | Production Implementation | Impact / Benchmark |
|---|---|---|
| Frontend Core | Next.js 15 App Router, React 19, TypeScript, Turbopack | Instant hydration and optimal client bundle splitting |
| Headless Content Store | Sanity.io Content Lake with GROQ projection queries | Zero server management and automatic global multi-region replication |
| Artificial Intelligence | Google Gemini 1.5 Flash via Server-Side Generative AI SDK | Cost-effective, highly deterministic startup venture evaluations |
| Theme & Styling | Tailwind CSS with CSS Variable dark/light mode toggle | Flicker-free initial paint matching user OS color preference |
I'm available for full-time roles & freelance projects.
I thrive on crafting dynamic web applications and delivering seamless user experiences.