Meta Description" name="description" />

Share this result

Previews are deleted daily. Get a permanent share link sent to your inbox:
Script
import React, { useState, useRef, useEffect } from 'react'; import { Upload, X, Image as ImageIcon, Sparkles, AlertCircle, Download, Banana, RefreshCw } from 'lucide-react'; const apiKey = ""; // API Key injected by environment export default function App() { const [childPhoto, setChildPhoto] = useState(null); const [adultPhoto, setAdultPhoto] = useState(null); const [generatedImage, setGeneratedImage] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [status, setStatus] = useState(''); // File handling helpers const handleFileChange = (e, setPhoto) => { const file = e.target.files[0]; if (file) { if (file.size > 5 * 1024 * 1024) { setError("File size too large. Please upload images under 5MB."); return; } const reader = new FileReader(); reader.onloadend = () => { setPhoto({ file, preview: reader.result, base64: reader.result.split(',')[1], mimeType: file.type }); setError(null); }; reader.readAsDataURL(file); } }; // API Call with Exponential Backoff const generateImage = async () => { if (!childPhoto || !adultPhoto) { setError("Please upload both photos to continue."); return; } setLoading(true); setError(null); setGeneratedImage(null); setStatus('Initializing Nano Banana Model...'); const prompt = ` Generate a realistic, high-quality image based on the two input images provided. The first image is a child. The second image is an adult. Create a heartwarming scene where the adult (from the second image) is holding hands with the child (from the first image). Key Requirements: 1. The interaction must look natural and emotional. 2. The person from the recent photo (adult) should be interacting with their younger self (child). 3. Use soft, studio-quality lighting. 4. The background must be a smooth, clean white/off-white background. 5. The style should be photorealistic. `; // Construct Payload const payload = { contents: [{ parts: [ { text: prompt }, { inlineData: { mimeType: childPhoto.mimeType, data: childPhoto.base64 } }, { inlineData: { mimeType: adultPhoto.mimeType, data: adultPhoto.base64 } } ] }], generationConfig: { responseModalities: ["TEXT", "IMAGE"] } }; const makeRequest = async (retryCount = 0) => { try { setStatus(retryCount > 0 ? `Optimizing pixels (Attempt ${retryCount + 1})...` : 'Processing temporal fusion...'); const response = await fetch( `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image-preview:generateContent?key=${apiKey}`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(payload) } ); if (!response.ok) { // Handle specific API errors if (response.status === 503) throw new Error("Model overloaded"); const errData = await response.json(); throw new Error(errData.error?.message || `API Error: ${response.status}`); } const result = await response.json(); // Extract Image const imagePart = result.candidates?.[0]?.content?.parts?.find(p => p.inlineData); if (imagePart) { const base64Image = `data:image/png;base64,${imagePart.inlineData.data}`; setGeneratedImage(base64Image); setStatus('Completed!'); } else { // Sometimes the model might refuse or return only text const textPart = result.candidates?.[0]?.content?.parts?.find(p => p.text); if (textPart) { throw new Error("The model returned text instead of an image. Please try a different photo."); } throw new Error("Failed to generate image. Please try again."); } } catch (err) { if (retryCount < 4) { const delay = Math.pow(2, retryCount) * 1000; await new Promise(r => setTimeout(r, delay)); return makeRequest(retryCount + 1); } else { throw err; } } }; try { await makeRequest(); } catch (err) { setError(err.message || "An unexpected error occurred."); } finally { setLoading(false); } }; const reset = () => { setChildPhoto(null); setAdultPhoto(null); setGeneratedImage(null); setError(null); setStatus(''); }; return ( <div className="min-h-screen bg-gray-50 text-gray-800 font-sans selection:bg-yellow-200"> {/* Header */} <header className="bg-white border-b border-gray-200 sticky top-0 z-10"> <div className="max-w-5xl mx-auto px-4 h-16 flex items-center justify-between"> <div className="flex items-center gap-2"> <div className="bg-yellow-400 p-2 rounded-lg text-white"> <Banana size={20} fill="currentColor" className="text-yellow-900" /> </div> <h1 className="text-xl font-bold tracking-tight text-gray-900">AiEdit</h1> <span className="px-2 py-0.5 bg-gray-100 text-gray-500 text-xs rounded-full font-medium border border-gray-200"> Nano Banana Model v1.0 </span> </div> <button onClick={reset} className="text-sm font-medium text-gray-500 hover:text-gray-900 flex items-center gap-2" > <RefreshCw size={14} /> Reset </button> </div> </header> <main className="max-w-5xl mx-auto px-4 py-12"> <div className="text-center mb-12"> <h2 className="text-4xl font-extrabold text-gray-900 mb-4 tracking-tight"> Meet your inner child. </h2> <p className="text-lg text-gray-600 max-w-2xl mx-auto"> Upload a childhood photo and a recent photo. Our Nano Banana AI will bridge the gap of time, creating a memory where you stand hand-in-hand with your younger self. </p> </div> {/* Error Message */} {error && ( <div className="mb-8 p-4 bg-red-50 border border-red-200 rounded-xl flex items-start gap-3 text-red-700 max-w-2xl mx-auto animate-in fade-in slide-in-from-top-4"> <AlertCircle className="shrink-0 mt-0.5" size={20} /> <div> <p className="font-semibold">Generation Failed</p> <p className="text-sm opacity-90">{error}</p> </div> <button onClick={() => setError(null)} className="ml-auto hover:bg-red-100 p-1 rounded"> <X size={16} /> </button> </div> )} {/* Main Interface Grid */} <div className="grid md:grid-cols-2 gap-8 items-start"> {/* Left Column: Inputs */} <div className="space-y-6"> {/* Child Photo Input */} <div className="bg-white p-6 rounded-2xl shadow-sm border border-gray-200 transition-all hover:shadow-md"> <div className="flex justify-between items-center mb-4"> <label className="text-sm font-bold text-gray-900 uppercase tracking-wide flex items-center gap-2"> <span className="w-2 h-2 rounded-full bg-yellow-400"></span> 1. Child Photo </label> {childPhoto && ( <button onClick={() => setChildPhoto(null)} className="text-gray-400 hover:text-red-500"> <X size={16} /> </button> )} </div> {!childPhoto ? ( <label className="block w-full cursor-pointer group"> <input type="file" className="hidden" accept="image/*" onChange={(e) => handleFileChange(e, setChildPhoto)} /> <div className="h-48 border-2 border-dashed border-gray-300 rounded-xl flex flex-col items-center justify-center gap-3 group-hover:border-yellow-400 group-hover:bg-yellow-50 transition-colors"> <div className="p-3 bg-gray-100 rounded-full group-hover:bg-white transition-colors"> <Upload className="text-gray-400 group-hover:text-yellow-500" size={24} /> </div> <p className="text-sm text-gray-500 font-medium group-hover:text-yellow-700">Upload old photo</p> </div> </label> ) : ( <div className="relative h-48 rounded-xl overflow-hidden bg-gray-100 ring-1 ring-gray-200"> <img src={childPhoto.preview} alt="Child" className="w-full h-full object-cover" /> </div> )} </div> {/* Adult Photo Input */} <div className="bg-white p-6 rounded-2xl shadow-sm border border-gray-200 transition-all hover:shadow-md"> <div className="flex justify-between items-center mb-4"> <label className="text-sm font-bold text-gray-900 uppercase tracking-wide flex items-center gap-2"> <span className="w-2 h-2 rounded-full bg-blue-500"></span> 2. Recent Photo </label> {adultPhoto && ( <button onClick={() => setAdultPhoto(null)} className="text-gray-400 hover:text-red-500"> <X size={16} /> </button> )} </div> {!adultPhoto ? ( <label className="block w-full cursor-pointer group"> <input type="file" className="hidden" accept="image/*" onChange={(e) => handleFileChange(e, setAdultPhoto)} /> <div className="h-48 border-2 border-dashed border-gray-300 rounded-xl flex flex-col items-center justify-center gap-3 group-hover:border-blue-400 group-hover:bg-blue-50 transition-colors"> <div className="p-3 bg-gray-100 rounded-full group-hover:bg-white transition-colors"> <Upload className="text-gray-400 group-hover:text-blue-500" size={24} /> </div> <p className="text-sm text-gray-500 font-medium group-hover:text-blue-700">Upload recent photo</p> </div> </label> ) : ( <div className="relative h-48 rounded-xl overflow-hidden bg-gray-100 ring-1 ring-gray-200"> <img src={adultPhoto.preview} alt="Adult" className="w-full h-full object-cover" /> </div> )} </div> <button onClick={generateImage} disabled={loading || !childPhoto || !adultPhoto} className={`w-full py-4 px-6 rounded-xl font-bold text-lg shadow-lg shadow-yellow-200/50 flex items-center justify-center gap-3 transition-all transform active:scale-95 ${loading || !childPhoto || !adultPhoto ? 'bg-gray-200 text-gray-400 cursor-not-allowed shadow-none' : 'bg-gradient-to-r from-yellow-400 to-yellow-500 text-white hover:from-yellow-500 hover:to-yellow-600' }`} > {loading ? ( <> <div className="w-5 h-5 border-3 border-white/30 border-t-white rounded-full animate-spin" /> <span>Processing...</span> </> ) : ( <> <Sparkles size={20} /> <span>Generate Time Bridge</span> </> )} </button> {loading && ( <p className="text-center text-sm text-gray-500 animate-pulse font-medium">{status}</p> )} </div> {/* Right Column: Result */} <div className="bg-white p-2 rounded-3xl shadow-xl shadow-gray-200/50 border border-gray-100 h-full min-h-[500px] flex flex-col"> <div className="flex-1 bg-gray-50 rounded-2xl border-2 border-dashed border-gray-200 flex flex-col items-center justify-center relative overflow-hidden"> {!generatedImage && !loading && ( <div className="text-center p-8 max-w-sm"> <div className="w-20 h-20 bg-white rounded-2xl shadow-sm mx-auto mb-6 flex items-center justify-center"> <ImageIcon className="text-gray-300" size={40} /> </div> <h3 className="text-gray-900 font-semibold mb-2">Ready to Imagine</h3> <p className="text-gray-500 text-sm">Upload your photos on the left and hit generate to see the magic happen here.</p> </div> )} {loading && !generatedImage && ( <div className="absolute inset-0 flex flex-col items-center justify-center bg-white/80 backdrop-blur-sm z-10"> <div className="relative w-24 h-24 mb-6"> <div className="absolute inset-0 border-4 border-yellow-200 rounded-full animate-ping opacity-25"></div> <div className="absolute inset-2 border-4 border-yellow-400 border-t-transparent rounded-full animate-spin"></div> <div className="absolute inset-0 flex items-center justify-center"> <Banana className="text-yellow-500 animate-bounce" size={32} /> </div> </div> <p className="text-gray-600 font-medium">Consulting Nano Banana...</p> </div> )} {generatedImage && ( <div className="relative w-full h-full group"> <img src={generatedImage} alt="Generated Result" className="w-full h-full object-contain bg-white" /> <div className="absolute inset-0 bg-black/0 group-hover:bg-black/10 transition-colors flex items-end justify-center pb-6 opacity-0 group-hover:opacity-100"> <a href={generatedImage} download="AiEdit_TimeBridge.png" className="bg-white text-gray-900 px-6 py-3 rounded-full font-bold shadow-lg flex items-center gap-2 transform translate-y-4 group-hover:translate-y-0 transition-all hover:bg-yellow-50" > <Download size={18} /> Download Memory </a> </div> </div> )} </div> {/* Footer inside the card */} <div className="p-6 text-center"> <p className="text-xs text-gray-400 uppercase tracking-widest font-semibold">AiEdit Result Preview</p> </div> </div> </div> </main> </div> ); }
Landing Page
This ad does not have a landing page available
Network Timeline
Performance Summary

4

Requests

1

Domains

15KB

Transfer Size

16KB

Content Size

84.0ms

Dom Content Loaded

204.0ms

First Paint

116.0ms

Load Time
Domain Breakdown
Transfer Size (bytes)
Loading...
Content Size (bytes)
Loading...
Header Size (bytes)
Loading...
Requests
Loading...
Timings (ms)
Loading...
Total Time
Loading...
Content Breakdown
Transfer Size (bytes)
Loading...
Content Size (bytes)
Loading...
Header Size (bytes)
Loading...
Requests
Loading...