Connect your API key
Enter your Anthropic API key to enable AI generation. Your key is stored only in your browser — never sent to any server except Anthropic's.
Get your free API key at console.anthropic.com → API Keys → Create Key. New accounts get free credits.
Keep your key private. Never paste it into public sites or share it.
Generate image prompts
Describe your vision and get a perfectly crafted prompt for any AI image tool.
What do you want to create?
0 characters
🎨
Midjourney🤖
DALL-E 3⚡
Stable Diff.🔥
Firefly🦁
Leonardo💡
Ideogram
Photorealistic
Cinematic
Anime
Oil painting
Digital art
Watercolor
3D render
Pencil sketch
Comic book
Pixel art
Dramatic
Serene
Dark & moody
Vibrant
Mystical
Minimalist
Epic
Nostalgic
Generated prompt
Generate video prompts
Craft detailed cinematic prompts for Sora, Runway, Kling, and all major video AI tools.
Describe your scene
0 characters
🌀
Sora✈️
Runway🎬
Kling⚡
Pika🌙
Luma🎥
Hailuo
Cinematic film
Documentary
Anime
Music video
Horror
Sci-fi
Slow motion
Time-lapse
Normal speed
Fast cuts
Generated video prompt
Image → Prompt
Upload any image and get the exact AI prompt needed to recreate or remix it.
🖼
Click to upload or drag an image here
PNG, JPG, WEBP, GIF — max 5MB
Midjourney
DALL-E 3
Stable Diffusion
Full description
Style keywords only
Standard
Deep — include colors & textures
Technical — camera & lens info
Extracted prompt
Prompt library
All your saved prompts. Click copy or load to reuse them.
📚
No saved prompts yet.
Generate some prompts and click "Save to library".
]) tabs
].classList.add('active');
if (mtabs
]) mtabs
].classList.add('active');
window.scrollTo(0, 0);
}// =====================
// TAG HELPERS
// =====================
function singleTag(el, groupId) {
document.querySelectorAll('#' + groupId + ' .tag').forEach(t => t.classList.remove('active'));
el.classList.toggle('active');
}
function multiTag(el) { el.classList.toggle('active'); }function getActiveTags(groupId) {
return Array.from(document.querySelectorAll('#' + groupId + ' .tag.active')).map(t => t.textContent);
}function selectPlatform(el, name) {
const grid = el.closest('.platform-grid');
grid.querySelectorAll('.platform-card').forEach(c => c.classList.remove('active'));
el.classList.add('active');
const page = el.closest('.page');
if (page && page.id === 'page-video') selectedPlatformVideo = name;
else selectedPlatformText = name;
}// =====================
// CHAR COUNT
// =====================
function updateCharCount(inputId, countId) {
const len = document.getElementById(inputId).value.length;
document.getElementById(countId).textContent = len + ' characters';
}// =====================
// API CALL
// =====================
async function callClaude(system, userMsg, imageData = null) {
if (!apiKey) {
openModal();
throw new Error('API key required. Please add your Anthropic API key.');
}
const content = [];
if (imageData) {
content.push({ type: 'image', source: { type: 'base64', media_type: imageData.type, data: imageData.data } });
}
content.push({ type: 'text', text: userMsg });const res = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': apiKey,
'anthropic-version': '2023-06-01'
},
body: JSON.stringify({
model: 'claude-haiku-4-5-20251001',
max_tokens: 1000,
system: system,
messages: [{ role: 'user', content: content }]
})
});
const data = await res.json();
if (data.error) throw new Error(data.error.message);
return data.content[0].text;
}function setLoading(btnId, statusId, on, msg = '') {
const btn = document.getElementById(btnId);
if (btn) btn.disabled = on;
const st = document.getElementById(statusId);
if (st) st.innerHTML = on ? ' ' + msg : msg;
}// =====================
// IMAGE PROMPT GENERATOR
// =====================
async function generateImagePrompt() {
const input = document.getElementById('text-input').value.trim();
if (!input) { alert('Please describe what you want to create first.'); return; }const style = getActiveTags('style-tags').join(', ') || 'not specified';
const mood = getActiveTags('mood-tags').join(', ') || 'not specified';
const ratio = document.getElementById('ratio').value;
const detail = document.getElementById('detail').value;
const platform = selectedPlatformText;document.getElementById('text-result-card').style.display = 'block';
document.getElementById('text-result').textContent = '';
document.getElementById('variant-btn').style.display = 'none';
setLoading('gen-btn', 'text-status', true, 'Generating your prompt...');try {
const system = `You are an expert AI image prompt engineer with deep knowledge of all major image generation platforms. Your prompts are highly detailed, specific, and optimized for the target platform. Output ONLY the final prompt — no explanation, no preamble, no quotes, no markdown.`;const user = `Generate an optimized ${platform} image generation prompt based on:Subject / scene: ${input}
Visual style: ${style}
Mood: ${mood}
Aspect ratio: ${ratio}
Detail level: ${detail}Requirements:
- Use ${platform}-specific syntax, parameters, and best practices
- Include lighting details, composition, camera/lens info if relevant
- Add quality boosters appropriate for ${platform}
- Keep it under 400 characters for Midjourney, longer for others
- Make it vivid and specific`;const result = await callClaude(system, user);
document.getElementById('text-result').textContent = result;
lastGeneratedText = result;
document.getElementById('variant-btn').style.display = 'inline-flex';
setLoading('gen-btn', 'text-status', false, '');
} catch(e) {
document.getElementById('text-result').textContent = 'Error: ' + e.message;
setLoading('gen-btn', 'text-status', false, '');
}
}async function generateVariant() {
if (!lastGeneratedText) return;
document.getElementById('text-result').textContent = '';
setLoading('gen-btn', 'text-status', true, 'Generating variant...');
try {
const result = await callClaude(
'You are an expert AI image prompt engineer. Create a creative variation of the given prompt — same subject, but different angle, lighting, or style twist. Output ONLY the new prompt, no explanation.',
'Create a fresh variation of this prompt:\n\n' + lastGeneratedText
);
document.getElementById('text-result').textContent = result;
lastGeneratedText = result;
setLoading('gen-btn', 'text-status', false, 'Variant ready');
} catch(e) {
setLoading('gen-btn', 'text-status', false, 'Error: ' + e.message);
}
}function clearTextPage() {
document.getElementById('text-input').value = '';
document.getElementById('text-chars').textContent = '0 characters';
document.querySelectorAll('#style-tags .tag, #mood-tags .tag').forEach(t => t.classList.remove('active'));
document.getElementById('text-result-card').style.display = 'none';
lastGeneratedText = '';
}// =====================
// VIDEO PROMPT GENERATOR
// =====================
async function generateVideoPrompt() {
const input = document.getElementById('video-input').value.trim();
if (!input) { alert('Please describe your scene first.'); return; }const camera = document.getElementById('camera').value;
const duration = document.getElementById('duration').value;
const lighting = document.getElementById('lighting').value;
const style = getActiveTags('vid-style-tags').join(', ') || 'cinematic';
const speed = getActiveTags('speed-tags').join(', ') || 'normal speed';
const platform = selectedPlatformVideo;document.getElementById('video-result-card').style.display = 'block';
document.getElementById('video-result').textContent = '';
setLoading('vid-gen-btn', 'video-status', true, 'Crafting your video prompt...');try {
const system = `You are an expert AI video prompt engineer specializing in text-to-video generation tools. You write detailed, cinematic prompts that produce stunning video results. Output ONLY the final prompt — no explanation, no preamble.`;const user = `Generate an optimized ${platform} video generation prompt:Scene: ${input}
Camera motion: ${camera}
Duration: ${duration}
Lighting: ${lighting}
Visual style: ${style}
Motion speed: ${speed}Requirements:
- Write for ${platform} specifically with platform-appropriate syntax
- Include detailed motion descriptions, atmosphere, and visual texture
- Specify subject movement, background elements, and transitions
- Add quality and resolution tags appropriate for ${platform}
- Make it cinematic and vivid`;const result = await callClaude(system, user);
document.getElementById('video-result').textContent = result;
lastGeneratedVideo = result;
setLoading('vid-gen-btn', 'video-status', false, '');
} catch(e) {
document.getElementById('video-result').textContent = 'Error: ' + e.message;
setLoading('vid-gen-btn', 'video-status', false, '');
}
}function clearVideoPage() {
document.getElementById('video-input').value = '';
document.getElementById('video-chars').textContent = '0 characters';
document.querySelectorAll('#vid-style-tags .tag, #speed-tags .tag').forEach(t => t.classList.remove('active'));
document.getElementById('video-result-card').style.display = 'none';
}// =====================
// IMAGE → PROMPT (REVERSE)
// =====================
function handleFile(file) {
if (!file) return;
if (file.size > 5 * 1024 * 1024) { alert('File too large. Please use an image under 5MB.'); return; }
const reader = new FileReader();
reader.onload = e => {
const src = e.target.result;
const preview = document.getElementById('img-preview');
preview.src = src;
preview.style.display = 'block';
document.getElementById('reverse-controls').style.display = 'block';
currentImageData = { data: src.split(',')[1], type: file.type || 'image/jpeg' };
document.getElementById('drop-zone').style.display = 'none';
};
reader.readAsDataURL(file);
}function handleDrop(e) {
e.preventDefault();
document.getElementById('drop-zone').classList.remove('drag');
const file = e.dataTransfer.files[0];
if (file && file.type.startsWith('image/')) handleFile(file);
}async function analyzeImage() {
if (!currentImageData) return;
const formats = getActiveTags('rev-format-tags');
const depth = getActiveTags('depth-tags').join('') || 'Standard';
const targetFormats = formats.length ? formats.join(', ') : 'Midjourney';document.getElementById('reverse-result-card').style.display = 'block';
document.getElementById('reverse-result').textContent = '';
setLoading('analyze-btn', 'reverse-status', true, 'Analyzing image...');try {
const system = `You are an expert AI image analyst and prompt reverse-engineer. Analyze images deeply and extract precise prompts that would recreate them. Output ONLY the prompt(s) — no preamble, no explanation.`;const user = `Analyze this image in full detail and generate a ${targetFormats} prompt that would recreate it as accurately as possible.Analysis depth: ${depth}Include ALL relevant details:
- Main subject and composition
- Art style and medium
- Color palette and lighting
- Mood and atmosphere
- Background and environment
- Camera angle and perspective
- Texture and surface details
- Any text or unique elements
- Platform-specific parameters and quality tags${formats.length > 1 ? 'Format the output with a label for each platform requested.' : ''}`;const result = await callClaude(system, user, currentImageData);
document.getElementById('reverse-result').textContent = result;
setLoading('analyze-btn', 'reverse-status', false, '');
} catch(e) {
document.getElementById('reverse-result').textContent = 'Error: ' + e.message;
setLoading('analyze-btn', 'reverse-status', false, '');
}
}function useAsImagePrompt() {
const text = document.getElementById('reverse-result').textContent;
document.getElementById('text-input').value = text;
showPage('text');
document.getElementById('text-chars').textContent = text.length + ' characters';
window.scrollTo(0, 0);
}// =====================
// LIBRARY
// =====================
function saveToLibrary(type) {
const ids = { image: 'text-result', video: 'video-result', reverse: 'reverse-result' };
const text = document.getElementById(ids[type]).textContent.trim();
if (!text || text.startsWith('Error:')) { alert('Nothing to save yet.'); return; }
library.unshift({ type, text, date: new Date().toLocaleDateString() });
localStorage.setItem('ps_library', JSON.stringify(library));
renderLibrary();
const btn = event.target;
const orig = btn.textContent;
btn.textContent = 'Saved!';
setTimeout(() => btn.textContent = orig, 1500);
}function renderLibrary() {
const el = document.getElementById('library-container');
if (!library.length) {
el.innerHTML = `
📚
No saved prompts yet.
Generate some prompts and click "Save to library".
${typeLabels[item.type] || item.type}
${item.date}
${escHtml(item.text.substring(0, 220))}${item.text.length > 220 ? '...' : ''}
