Spaces:
Running
Running
<html><head><base href="https://websim.ai/storytelling-sidekick"><title>Narrative Nexus: Your Storytelling Companion</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<style> | |
@keyframes twinkle { | |
0% { opacity: 1; } | |
50% { opacity: 0.5; } | |
100% { opacity: 1; } | |
} | |
.star { | |
position: absolute; | |
background-color: white; | |
border-radius: 50%; | |
animation: twinkle 2s infinite; | |
} | |
</style> | |
</head> | |
<body class="bg-gradient-to-br from-indigo-900 to-purple-900 text-white min-h-screen font-sans"> | |
<div id="starfield" class="fixed inset-0 z-0"></div> | |
<header class="relative z-10 py-6"> | |
<div class="container mx-auto px-4"> | |
<h1 class="text-4xl font-bold text-center">Narrative Nexus</h1> | |
<p class="mt-2 text-center text-purple-200">Your Collaborative Storytelling Companion</p> | |
</div> | |
</header> | |
<main class="container mx-auto px-4 py-8 relative z-10"> | |
<div class="bg-white bg-opacity-10 backdrop-filter backdrop-blur-lg rounded-lg shadow-lg p-6 mb-8"> | |
<h2 class="text-2xl font-semibold mb-4">Story Canvas</h2> | |
<div id="story-container" class="space-y-4"> | |
<div id="story-content" class="prose prose-invert max-w-none"> | |
<p>Once upon a time, in a world not unlike our own...</p> | |
</div> | |
<div id="user-input" class="mt-4"> | |
<textarea id="user-contribution" rows="3" class="w-full px-3 py-2 text-gray-700 border rounded-lg focus:outline-none" placeholder="Continue the story..."></textarea> | |
<button id="submit-contribution" class="mt-2 px-4 py-2 bg-purple-600 text-white rounded-md hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-opacity-50"> | |
Add to Story | |
</button> | |
</div> | |
</div> | |
</div> | |
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
<div class="bg-white bg-opacity-10 backdrop-filter backdrop-blur-lg rounded-lg shadow-lg p-6"> | |
<h3 class="text-xl font-semibold mb-3">Plot Twist Generator</h3> | |
<button id="generate-twist" class="w-full px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-opacity-50"> | |
Suggest a Plot Twist | |
</button> | |
<div id="plot-twist" class="mt-4 italic"></div> | |
</div> | |
<div class="bg-white bg-opacity-10 backdrop-filter backdrop-blur-lg rounded-lg shadow-lg p-6"> | |
<h3 class="text-xl font-semibold mb-3">Character Development</h3> | |
<button id="develop-character" class="w-full px-4 py-2 bg-pink-600 text-white rounded-md hover:bg-pink-700 focus:outline-none focus:ring-2 focus:ring-pink-500 focus:ring-opacity-50"> | |
Develop a Character | |
</button> | |
<div id="character-development" class="mt-4"></div> | |
</div> | |
</div> | |
</main> | |
<script> | |
// Create starfield effect | |
const starfield = document.getElementById('starfield'); | |
for (let i = 0; i < 100; i++) { | |
const star = document.createElement('div'); | |
star.className = 'star'; | |
star.style.width = `${Math.random() * 3}px`; | |
star.style.height = star.style.width; | |
star.style.left = `${Math.random() * 100}%`; | |
star.style.top = `${Math.random() * 100}%`; | |
star.style.animationDelay = `${Math.random() * 2}s`; | |
starfield.appendChild(star); | |
} | |
let storyContext = { | |
characters: [], | |
plotPoints: [], | |
setting: "a world not unlike our own" | |
}; | |
document.getElementById('submit-contribution').addEventListener('click', function() { | |
const userContribution = document.getElementById('user-contribution').value; | |
if (userContribution.trim() !== '') { | |
addToStory(userContribution); | |
document.getElementById('user-contribution').value = ''; | |
} | |
}); | |
document.getElementById('generate-twist').addEventListener('click', generatePlotTwist); | |
document.getElementById('develop-character').addEventListener('click', developCharacter); | |
function addToStory(contribution) { | |
const storyContent = document.getElementById('story-content'); | |
const newParagraph = document.createElement('p'); | |
newParagraph.textContent = contribution; | |
storyContent.appendChild(newParagraph); | |
// Update story context | |
updateStoryContext(contribution); | |
// AI response | |
setTimeout(() => { | |
const aiResponse = generateAIResponse(); | |
const aiParagraph = document.createElement('p'); | |
aiParagraph.textContent = aiResponse; | |
aiParagraph.className = 'text-green-300 italic'; | |
storyContent.appendChild(aiParagraph); | |
}, 1000); | |
} | |
function updateStoryContext(text) { | |
// Simple context update - in a real app, this would be more sophisticated | |
const words = text.toLowerCase().split(' '); | |
const characterNames = words.filter(word => word.length > 2 && word[0] === word[0].toUpperCase()); | |
storyContext.characters = [...new Set([...storyContext.characters, ...characterNames])]; | |
storyContext.plotPoints.push(text); | |
} | |
function generateAIResponse() { | |
// This is a simplified AI response. In a real application, this would be much more complex and context-aware. | |
const responses = [ | |
"The plot thickens as an unexpected visitor arrives.", | |
"Meanwhile, in a distant part of the kingdom...", | |
"Little did they know, their actions had set in motion a chain of events that would change everything.", | |
"As night fell, the true nature of their quest became clear.", | |
"Suddenly, a loud noise broke the silence, startling everyone." | |
]; | |
return responses[Math.floor(Math.random() * responses.length)]; | |
} | |
function generatePlotTwist() { | |
const twists = [ | |
"A trusted ally is revealed to be the true antagonist.", | |
"A long-lost relative of the protagonist suddenly appears.", | |
"The characters discover they're in a simulation or alternate reality.", | |
"A major character is not who they claim to be.", | |
"An impending natural disaster threatens to derail the entire mission.", | |
"The antagonist and protagonist must work together to face a greater threat." | |
]; | |
const twist = twists[Math.floor(Math.random() * twists.length)]; | |
document.getElementById('plot-twist').textContent = twist; | |
} | |
function developCharacter() { | |
const traits = ["brave", "cunning", "compassionate", "reckless", "wise", "mysterious"]; | |
const backgrounds = ["orphaned at a young age", "raised by wolves", "a former aristocrat", "a time traveler", "the chosen one", "a retired assassin"]; | |
const goals = ["seek revenge", "find true love", "save the world", "discover the truth about their past", "gain ultimate power", "find inner peace"]; | |
const character = { | |
name: generateName(), | |
trait: traits[Math.floor(Math.random() * traits.length)], | |
background: backgrounds[Math.floor(Math.random() * backgrounds.length)], | |
goal: goals[Math.floor(Math.random() * goals.length)] | |
}; | |
const characterDiv = document.getElementById('character-development'); | |
characterDiv.innerHTML = ` | |
<p><strong>${character.name}</strong> is a ${character.trait} individual who was ${character.background}. Their ultimate goal is to ${character.goal}.</p> | |
`; | |
storyContext.characters.push(character.name); | |
} | |
function generateName() { | |
const prefixes = ["Ar", "Bel", "Cae", "Dae", "El", "Fae", "Gal", "Hy", "Il", "Jor", "Ky", "Lum", "Mor", "Nyx", "Ob", "Pyx", "Quin", "Rae", "Syl", "Tae", "Um", "Vex", "Wynn", "Xan", "Yen", "Zeph"]; | |
const suffixes = ["ius", "ana", "on", "ia", "or", "en", "yn", "is", "ira", "ara", "ola", "ine", "aki", "ith", "oth", "wen", "wyn", "lin", "ron", "ton", "kin", "fel", "wel", "tal"]; | |
return prefixes[Math.floor(Math.random() * prefixes.length)] + suffixes[Math.floor(Math.random() * suffixes.length)]; | |
} | |
</script> | |
</body></html> |