Advanced Molecular Narrative Engine v3.0
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: ‘Courier New’, monospace;
background: linear-gradient(135deg, #0a0a0a, #1a1a2e, #16213e);
color: #e0e0e0;
line-height: 1.4;
min-height: 100vh;
overflow-x: hidden;
}
.container {
max-width: 1600px;
margin: 0 auto;
padding: 20px;
}
.main-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 20px;
}
@media (max-width: 1200px) {
.main-grid {
grid-template-columns: 1fr;
}
}
.panel {
background: rgba(0, 0, 0, 0.9);
padding: 25px;
border-radius: 15px;
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.8);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.chromosome {
background: rgba(0, 30, 0, 0.4);
padding: 15px;
border-radius: 8px;
margin: 10px 0;
border: 1px solid #2e7d32;
position: relative;
transition: all 0.3s ease;
}
.chromosome:hover {
border-color: #4caf50;
box-shadow: 0 0 15px rgba(76, 175, 80, 0.3);
}
.chromosome-label {
position: absolute;
top: -10px;
left: 15px;
background: #0a0a0a;
padding: 0 8px;
color: #4caf50;
font-size: 0.8em;
font-weight: bold;
}
.gene {
background: rgba(76, 175, 80, 0.2);
border: 1px solid #4caf50;
border-radius: 4px;
margin: 5px 0;
padding: 8px;
position: relative;
transition: all 0.3s ease;
cursor: pointer;
}
.gene:hover {
transform: translateX(5px);
}
.gene.active {
border-color: #8bc34a;
background: rgba(139, 195, 74, 0.3);
animation: pulse 2s infinite;
}
.gene.silenced {
border-color: #666;
background: rgba(100, 100, 100, 0.2);
opacity: 0.5;
}
.gene.enhanced {
border-color: #ffeb3b;
background: rgba(255, 235, 59, 0.3);
box-shadow: 0 0 8px rgba(255, 235, 59, 0.3);
animation: glow 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.8; }
}
@keyframes glow {
0%, 100% { box-shadow: 0 0 8px rgba(255, 235, 59, 0.3); }
50% { box-shadow: 0 0 15px rgba(255, 235, 59, 0.5); }
}
.promoter {
background: #1976d2;
color: white;
padding: 2px 6px;
border-radius: 3px;
font-size: 0.7em;
margin-right: 5px;
}
.enhancer {
background: #ff9800;
color: white;
padding: 2px 6px;
border-radius: 3px;
font-size: 0.7em;
margin-right: 5px;
}
.silencer {
background: #f44336;
color: white;
padding: 2px 6px;
border-radius: 3px;
font-size: 0.7em;
margin-right: 5px;
}
.transcription-unit {
background: rgba(156, 39, 176, 0.2);
border: 1px solid #9c27b0;
border-radius: 6px;
padding: 12px;
margin: 10px 0;
position: relative;
overflow: hidden;
}
.transcription-unit::before {
content: ”;
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(156, 39, 176, 0.3), transparent);
animation: transcribe 3s infinite;
}
@keyframes transcribe {
100% { left: 100%; }
}
.mrna-strand {
background: rgba(156, 39, 176, 0.3);
padding: 10px;
border-radius: 6px;
margin: 8px 0;
font-family: ‘Courier New’, monospace;
letter-spacing: 1px;
font-size: 0.9em;
position: relative;
overflow: hidden;
}
.splice-variant {
background: rgba(233, 30, 99, 0.2);
border: 1px solid #e91e63;
border-radius: 4px;
padding: 8px;
margin: 5px 0;
transition: all 0.3s ease;
}
.splice-variant:hover {
background: rgba(233, 30, 99, 0.3);
}
.protein-complex {
background: rgba(63, 81, 181, 0.2);
border: 1px solid #3f51b5;
border-radius: 6px;
padding: 15px;
margin: 10px 0;
}
.story-output {
background: rgba(255, 255, 255, 0.05);
padding: 25px;
border-radius: 10px;
margin: 20px 0;
font-family: Georgia, serif;
line-height: 1.8;
min-height: 300px;
border-left: 4px solid #4caf50;
font-size: 1.1em;
color: #f0f0f0;
}
.story-output p {
margin-bottom: 15px;
}
.story-output .chapter-title {
font-size: 1.3em;
color: #81c784;
margin: 20px 0 10px 0;
font-weight: bold;
}
.regulatory-network {
background: rgba(0, 0, 50, 0.3);
border: 1px solid #2196f3;
border-radius: 8px;
padding: 15px;
margin: 15px 0;
}
.pathway-diagram {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 10px;
margin: 15px 0;
}
.pathway-step {
background: rgba(255, 255, 255, 0.1);
padding: 10px;
border-radius: 5px;
text-align: center;
border: 1px solid #607d8b;
position: relative;
transition: all 0.3s ease;
}
.pathway-step.active {
border-color: #4caf50;
background: rgba(76, 175, 80, 0.2);
transform: scale(1.05);
}
.controls {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 12px;
margin: 25px 0;
}
.btn {
background: linear-gradient(45deg, #1976d2, #1565c0);
color: white;
border: none;
padding: 12px 20px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.btn::before {
content: ”;
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
transform: translate(-50%, -50%);
transition: width 0.5s, height 0.5s;
}
.btn:hover::before {
width: 300px;
height: 300px;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
}
.btn.danger { background: linear-gradient(45deg, #d32f2f, #c62828); }
.btn.success { background: linear-gradient(45deg, #388e3c, #2e7d32); }
.btn.warning { background: linear-gradient(45deg, #f57c00, #ef6c00); }
.btn.special { background: linear-gradient(45deg, #6a1b9a, #4a148c); }
.molecular-log {
background: rgba(0, 0, 0, 0.5);
padding: 15px;
border-radius: 8px;
margin: 15px 0;
max-height: 250px;
overflow-y: auto;
font-size: 0.85em;
border: 1px solid #37474f;
}
.molecular-log::-webkit-scrollbar {
width: 8px;
}
.molecular-log::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.3);
}
.molecular-log::-webkit-scrollbar-thumb {
background: #4caf50;
border-radius: 4px;
}
.log-entry {
padding: 4px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.log-entry.error { color: #f44336; }
.log-entry.success { color: #4caf50; }
.log-entry.warning { color: #ff9800; }
.population-stats {
background: rgba(0, 30, 30, 0.4);
padding: 15px;
border-radius: 8px;
margin: 15px 0;
border: 1px solid #00695c;
}
.stat-row {
display: flex;
justify-content: space-between;
padding: 5px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.stat-row:last-child {
border-bottom: none;
}
.fitness-score {
color: #4caf50;
font-weight: bold;
font-size: 1.2em;
}
.epigenetic-marks {
background: rgba(255, 193, 7, 0.1);
border: 1px solid #ffc107;
border-radius: 4px;
padding: 8px;
margin: 8px 0;
font-size: 0.8em;
}
h1 {
text-align: center;
color: #4caf50;
margin-bottom: 20px;
text-shadow: 0 0 20px rgba(76, 175, 80, 0.5);
font-size: 2.5em;
}
h2 {
color: #81c784;
margin: 20px 0 10px 0;
font-size: 1.2em;
}
.section-title {
color: #81c784;
font-weight: bold;
margin: 25px 0 15px 0;
border-bottom: 2px solid #4caf50;
padding-bottom: 8px;
font-size: 1.1em;
}
.base-pair {
display: inline-block;
margin: 0 1px;
padding: 1px 3px;
border-radius: 2px;
font-size: 0.9em;
font-weight: bold;
}
.base-A { background: #f44336; color: white; }
.base-T { background: #2196f3; color: white; }
.base-G { background: #ff9800; color: white; }
.base-C { background: #4caf50; color: white; }
.base-U { background: #9c27b0; color: white; }
.reading-frame {
margin: 8px 0;
padding: 8px;
background: rgba(255, 255, 255, 0.05);
border-radius: 4px;
border-left: 3px solid #607d8b;
}
.reading-frame.active {
border-left-color: #4caf50;
background: rgba(76, 175, 80, 0.1);
}
.evolution-tree {
background: rgba(0, 0, 0, 0.5);
padding: 20px;
border-radius: 10px;
margin: 20px 0;
min-height: 200px;
border: 1px solid #37474f;
}
.generation-node {
background: rgba(255, 255, 255, 0.1);
padding: 10px;
border-radius: 5px;
margin: 5px;
display: inline-block;
border: 1px solid #4caf50;
}
.save-controls {
position: fixed;
bottom: 20px;
right: 20px;
display: flex;
gap: 10px;
z-index: 1000;
}
.save-btn {
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px 20px;
border-radius: 25px;
border: 1px solid #4caf50;
cursor: pointer;
transition: all 0.3s ease;
}
.save-btn:hover {
background: #4caf50;
transform: scale(1.05);
}
/* Loading animation */
.loading {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 9999;
}
.loading.active {
display: block;
}
.dna-strand {
width: 60px;
height: 60px;
position: relative;
animation: rotate 2s linear infinite;
}
.dna-strand::before,
.dna-strand::after {
content: ”;
position: absolute;
width: 100%;
height: 100%;
border: 3px solid #4caf50;
border-radius: 50%;
opacity: 0.6;
}
.dna-strand::after {
animation: scale 2s linear infinite;
}
@keyframes rotate {
100% { transform: rotate(360deg); }
}
@keyframes scale {
0%, 100% { transform: scale(1); opacity: 0.6; }
50% { transform: scale(1.2); opacity: 0.3; }
}
/* Tooltip */
.tooltip {
position: relative;
display: inline-block;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 200px;
background-color: rgba(0, 0, 0, 0.9);
color: #fff;
text-align: center;
border-radius: 6px;
padding: 8px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -100px;
opacity: 0;
transition: opacity 0.3s;
font-size: 0.8em;
border: 1px solid #4caf50;
}
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
/* Modal for gene details */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.8);
}
.modal-content {
background-color: #0a0a0a;
margin: 10% auto;
padding: 20px;
border: 1px solid #4caf50;
width: 80%;
max-width: 600px;
border-radius: 10px;
position: relative;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close:hover,
.close:focus {
color: #4caf50;
}
Advanced Molecular Narrative Engine v3.0
A complex genetic storytelling system with multi-chromosome organisms, gene regulation, evolution, and emergent narratives
No evolutionary history yet…
No regulatory activity…
No genes being expressed…
Organism not yet expressed…
Transcribe Active Genes
Alternative Splicing
Translate & Express
Mutate Genome
Epigenetic Changes
Environmental Pressure
Sexual Reproduction
Horizontal Transfer
Complete Life Cycle
Evolve 10 Generations
Analyze Genome
// Enhanced genetic architecture
const geneTypes = {
structural: [‘protagonist’, ‘antagonist’, ‘mentor’, ‘trickster’, ‘guardian’, ‘herald’, ‘shapeshifter’, ‘shadow’],
regulatory: [’emotion_enhancer’, ‘tension_promoter’, ‘pacing_silencer’, ‘style_modulator’, ‘theme_amplifier’, ‘mood_regulator’],
environmental: [‘time_period’, ‘location’, ‘atmosphere’, ‘weather’, ‘season’, ‘dimension’, ‘realm’],
metabolic: [‘plot_catalyst’, ‘character_development’, ‘theme_synthesis’, ‘narrative_energy’, ‘conflict_resolver’, ‘mystery_builder’]
};
const narrativeThemes = {
heroic: [‘courage’, ‘sacrifice’, ‘honor’, ‘destiny’],
tragic: [‘hubris’, ‘fate’, ‘loss’, ‘redemption’],
romantic: [‘love’, ‘passion’, ‘longing’, ‘union’],
mystery: [‘secrets’, ‘revelation’, ‘deception’, ‘truth’],
cosmic: [‘infinity’, ‘consciousness’, ‘existence’, ‘transcendence’]
};
const geneticCode = {
// Start and stop codons
‘ATG’: { start: true, element: ‘begins_journey’ },
‘TAA’: { stop: true }, ‘TAG’: { stop: true }, ‘TGA’: { stop: true },
// Character actions (A-heavy codons)
‘AAA’: ‘hero_awakens_from_dark_dreams’,
‘AAT’: ‘hero_decides_their_fate’,
‘AAG’: ‘hero_searches_for_truth’,
‘AAC’: ‘hero_discovers_hidden_power’,
‘ATA’: ‘villain_emerges_from_shadows’,
‘ATT’: ‘villain_threatens_the_realm’,
‘ATC’: ‘villain_retreats_to_darkness’,
‘ATG’: ‘journey_begins_at_dawn’,
‘AGA’: ‘allies_gather_in_secret’,
‘AGT’: ‘allies_pledge_their_support’,
‘AGG’: ‘crowd_witnesses_the_event’,
‘AGC’: ‘crowd_flees_in_terror’,
‘ACA’: ‘power_manifests_unexpectedly’,
‘ACT’: ‘magic_activates_ancient_seals’,
‘ACG’: ‘energy_flows_through_leylines’,
‘ACC’: ‘tension_builds_to_breaking’,
// Environmental elements (G-heavy codons)
‘GGG’: ‘within_ancient_forest_depths’,
‘GGA’: ‘inside_hidden_library_halls’,
‘GGT’: ‘among_forgotten_ruins’,
‘GGC’: ‘beneath_sacred_temple_stones’,
‘GAA’: ‘under_starlit_sky’,
‘GAT’: ‘through_deep_cavern_passages’,
‘GAG’: ‘across_mystical_labyrinth’,
‘GAC’: ‘beside_flowing_river’,
‘GTA’: ‘atop_towering_mountains’,
‘GTT’: ‘across_endless_desert_sands’,
‘GTG’: ‘within_misty_valley’,
‘GTC’: ‘upon_floating_city_platforms’,
‘GCA’: ‘at_reality_edge’,
‘GCT’: ‘where_time_nexus_forms’,
‘GCG’: ‘in_chaos_realm’,
‘GCC’: ‘through_void_space’,
// Relationships and emotions (C-heavy codons)
‘CCC’: ‘wise_mentor_shares_ancient_wisdom’,
‘CCA’: ‘enemy_reveals_true_nature’,
‘CCT’: ‘friend_offers_desperate_help’,
‘CCG’: ‘stranger_appears_with_warning’,
‘CAA’: ‘lost_child_cries_for_help’,
‘CAT’: ‘guardian_stands_firm’,
‘CAG’: ‘hero_falls_but_rises’,
‘CAC’: ‘darkness_consumes_the_light’,
‘CTA’: ‘love_blooms_against_odds’,
‘CTT’: ‘betrayal_cuts_deepest’,
‘CTG’: ‘sacrifice_changes_everything’,
‘CTC’: ‘redemption_arrives_at_last’,
‘CGA’: ‘truth_shatters_illusions’,
‘CGT’: ‘secrets_whisper_through_shadows’,
‘CGG’: ‘defiance_echoes_through_halls’,
‘CGC’: ‘mystery_unveils_its_face’,
// Mental states and transformations (T-heavy codons)
‘TTT’: ‘memories_flood_consciousness’,
‘TTA’: ‘realization_dawns_slowly’,
‘TTG’: ‘purpose_crystallizes_clearly’,
‘TTC’: ‘fear_grips_the_heart’,
‘TCT’: ‘hope_rises_like_phoenix’,
‘TCA’: ‘belief_strengthens_resolve’,
‘TCG’: ‘doubt_clouds_judgment’,
‘TCC’: ‘destiny_becomes_clear’,
‘TAT’: ‘visions_reveal_future’,
‘TAC’: ‘imagination_creates_reality’,
‘TGT’: ‘passion_ignites_souls’,
‘TGC’: ‘hatred_poisons_minds’,
‘TGG’: ‘desire_drives_action’,
‘TGA’: ‘transformation_completes_cycle’,
// Additional narrative elements for more variety
‘GGT’: ‘storm_gathers_strength’,
‘CTG’: ‘promise_binds_fate’,
‘GAT’: ‘shadow_lengthens_ominously’,
‘AGT’: ‘warrior_raises_sword’,
‘TCA’: ‘prophecy_unfolds_mysteriously’,
‘ACG’: ‘bridge_spans_worlds’,
‘CGT’: ‘whispers_echo_endlessly’,
‘GTC’: ‘portal_opens_suddenly’,
‘TAG’: ‘chapter_ends_dramatically’,
‘CAG’: ‘choice_defines_destiny’,
‘TGC’: ‘curse_takes_hold’,
‘GAC’: ‘river_changes_course’,
‘ATC’: ‘door_closes_forever’,
‘CGC’: ‘riddle_demands_answer’,
‘TAC’: ‘dream_becomes_reality’,
‘GCT’: ‘time_stops_momentarily’,
‘ACC’: ‘power_corrupts_absolutely’,
‘TCC’: ‘fate_seals_itself’,
‘CTC’: ‘bond_strengthens_eternally’,
‘GCC’: ‘void_gazes_back’
};
// Enhanced Chromosome class
class Chromosome {
constructor(id, length = 200, sequence = null) {
this.id = id;
this.sequence = sequence || this.generateSequence(length);
this.genes = this.identifyGenes();
this.regulatoryElements = this.generateRegulatoryElements();
this.epigeneticMarks = new Map();
this.methylationPattern = new Array(this.sequence.length).fill(false);
this.histoneModifications = new Map();
this.chromosomalRearrangements = [];
}
generateSequence(length) {
const bases = [‘A’, ‘T’, ‘G’, ‘C’];
let sequence = ”;
// Add promoter regions
sequence += ‘TATAAA’; // TATA box
// Generate sequence with structured gene regions
let i = 6;
while (i < length) {
// Increase probability of start codons periodically
if (i % 25 === 0 && Math.random() < 0.8) {
sequence += 'ATG';
i += 3;
// Generate gene body with appropriate length
const geneLength = 30 + Math.floor(Math.random() * 60);
for (let j = 0; j < geneLength && i < length; j++, i++) {
sequence += bases[Math.floor(Math.random() * bases.length)];
}
// Add stop codon
if (i < length – 3) {
const stopCodons = ['TAA', 'TAG', 'TGA'];
sequence += stopCodons[Math.floor(Math.random() * stopCodons.length)];
i += 3;
}
} else {
sequence += bases[Math.floor(Math.random() * bases.length)];
i++;
}
}
return sequence;
}
identifyGenes() {
const genes = [];
let i = 0;
let geneCount = 0;
while (i < this.sequence.length – 6) {
if (this.sequence.substring(i, i + 3) === 'ATG') {
let j = i + 3;
let geneSequence = 'ATG';
let foundStop = false;
// Find stop codon
while (j = 15) {
const geneTypeKeys = Object.keys(geneTypes);
// More intelligent gene type assignment based on sequence characteristics
let geneType;
const gcContent = (geneSequence.match(/[GC]/g) || []).length / geneSequence.length;
if (gcContent > 0.6) {
geneType = ‘environmental’; // GC-rich genes tend to be environmental
} else if (gcContent 60) {
geneType = ‘metabolic’; // Longer genes for complex processes
} else {
geneType = geneTypeKeys[geneCount % geneTypeKeys.length];
}
const geneOptions = geneTypes[geneType];
const geneName = geneOptions[Math.floor(Math.random() * geneOptions.length)];
genes.push({
id: `${this.id}_gene_${geneCount}`,
name: geneName,
type: geneType,
start: i,
end: j + 3,
sequence: geneSequence,
expression: 0.3 + Math.random() * 0.7, // Wider expression range
spliceVariants: this.generateSpliceVariants(geneSequence),
mutations: [],
conservationScore: Math.random()
});
geneCount++;
i = j + 3;
} else {
i++;
}
} else {
i++;
}
}
return genes;
}
generateSpliceVariants(sequence) {
const variants = [{
sequence: sequence,
name: ‘canonical’,
efficiency: 1.0
}];
if (sequence.length > 30) {
// Exon skipping variant
const skipStart = Math.floor(sequence.length * 0.3);
const skipEnd = Math.floor(sequence.length * 0.7);
variants.push({
sequence: sequence.substring(0, skipStart) + sequence.substring(skipEnd),
name: ‘exon_skip’,
efficiency: 0.8
});
// Alternative 3′ site
const altEnd = Math.floor(sequence.length * 0.8);
variants.push({
sequence: sequence.substring(0, altEnd) + ‘TAA’,
name: ‘alt_3prime’,
efficiency: 0.7
});
// Alternative 5′ site
if (sequence.length > 40) {
const altStart = 9;
variants.push({
sequence: ‘ATG’ + sequence.substring(altStart),
name: ‘alt_5prime’,
efficiency: 0.6
});
}
}
return variants;
}
generateRegulatoryElements() {
const elements = [];
for (let i = 0; i < this.genes.length; i++) {
const gene = this.genes[i];
// Core promoter
elements.push({
type: 'promoter',
subtype: 'core',
position: Math.max(0, gene.start – 30),
strength: 0.7 + Math.random() * 0.3,
target: gene.id,
sequence: 'TATAAA'
});
// Proximal promoter elements
if (Math.random() < 0.7) {
elements.push({
type: 'promoter',
subtype: 'proximal',
position: Math.max(0, gene.start – 100),
strength: Math.random(),
target: gene.id,
sequence: 'CAAT'
});
}
// Enhancers (can be far from gene)
if (Math.random() < 0.6) {
elements.push({
type: 'enhancer',
position: Math.floor(Math.random() * this.sequence.length),
strength: Math.random(),
target: gene.id,
distance_effect: true
});
}
// Silencers
if (Math.random() < 0.3) {
elements.push({
type: 'silencer',
position: Math.floor(Math.random() * this.sequence.length),
strength: Math.random(),
target: gene.id
});
}
// Insulators
if (Math.random() < 0.2) {
elements.push({
type: 'insulator',
position: Math.floor(Math.random() * this.sequence.length),
strength: Math.random(),
blocks: [gene.id]
});
}
}
return elements;
}
applyMethylation(position, length) {
for (let i = position; i < Math.min(position + length, this.methylationPattern.length); i++) {
this.methylationPattern[i] = true;
}
}
addHistoneModification(position, modification) {
if (!this.histoneModifications.has(position)) {
this.histoneModifications.set(position, []);
}
this.histoneModifications.get(position).push(modification);
}
}
// Enhanced Organism class
class Organism {
constructor(parentA = null, parentB = null) {
this.id = generateOrganismId();
this.chromosomes = [];
if (parentA && parentB) {
// Sexual reproduction
this.chromosomes = this.recombineChromosomes(parentA, parentB);
this.generation = Math.max(parentA.generation, parentB.generation) + 1;
this.parents = [parentA.id, parentB.id];
} else {
// De novo generation
this.chromosomes = [
new Chromosome('Chr1-Narrative', 300),
new Chromosome('Chr2-Characters', 280),
new Chromosome('Chr3-Environment', 260),
new Chromosome('Chr4-Regulation', 240),
new Chromosome('Chr5-Themes', 220),
new Chromosome('Chr6-Metacognition', 200)
];
this.generation = 1;
this.parents = [];
}
this.transcripts = new Map();
this.proteins = new Map();
this.metabolicState = {
energy: 100,
narrativeCoherence: 0,
characterDepth: 0,
plotComplexity: 0,
thematicResonance: 0
};
this.fitness = 0;
this.environmentalFactors = new Map();
this.phenotype = {
genre: null,
tone: null,
pacing: null,
themes: []
};
this.mutationRate = 0.01;
this.age = 0;
this.reproductiveSuccess = 0;
}
recombineChromosomes(parentA, parentB) {
const newChromosomes = [];
for (let i = 0; i < Math.min(parentA.chromosomes.length, parentB.chromosomes.length); i++) {
const chrA = parentA.chromosomes[i];
const chrB = parentB.chromosomes[i];
// Homologous recombination
const crossoverPoints = this.generateCrossoverPoints(chrA.sequence.length);
let newSequence = '';
let useA = true;
for (let j = 0; j < chrA.sequence.length; j++) {
if (crossoverPoints.includes(j)) {
useA = !useA;
}
if (j {
if (Math.random() < 0.7) {
newChr.epigeneticMarks.set(key, value);
}
});
newChromosomes.push(newChr);
}
return newChromosomes;
}
generateCrossoverPoints(length) {
const points = [];
const numCrossovers = 1 + Math.floor(Math.random() * 3);
for (let i = 0; i a – b);
}
getAllGenes() {
return this.chromosomes.flatMap(chr => chr.genes);
}
getActiveGenes() {
return this.getAllGenes().filter(gene => gene.expression > 0.3);
}
calculateGeneFitness() {
let totalFitness = 0;
let geneCount = 0;
const geneBalance = {};
// Count genes by type
this.chromosomes.forEach(chr => {
chr.genes.forEach(gene => {
if (!geneBalance[gene.type]) {
geneBalance[gene.type] = 0;
}
geneBalance[gene.type]++;
// Base fitness from expression
let geneFitness = gene.expression;
// Conservation bonus
geneFitness *= (1 + gene.conservationScore * 0.5);
// Type-specific bonuses
if (gene.type === ‘structural’) geneFitness *= 1.5;
if (gene.type === ‘regulatory’) geneFitness *= 1.2;
// Penalty for extreme expression
if (gene.expression > 0.9) geneFitness *= 0.8;
if (gene.expression = 4 ? 1.2 : 1.0;
// Metabolic state contribution
const metabolicFitness = (
this.metabolicState.narrativeCoherence * 0.3 +
this.metabolicState.characterDepth * 0.2 +
this.metabolicState.plotComplexity * 0.2 +
this.metabolicState.thematicResonance * 0.3
) / 100;
this.fitness = (totalFitness / geneCount) * balanceBonus * (0.7 + 0.3 * metabolicFitness);
// Age penalty
this.fitness *= Math.pow(0.95, this.age);
return this.fitness;
}
ageOrganism() {
this.age++;
this.metabolicState.energy *= 0.9;
// Accumulate mutations with age
if (Math.random() {
document.getElementById(‘loading’).classList.remove(‘active’);
}, 300);
}
function generateOrganism() {
showLoading();
currentOrganism = new Organism();
currentOrganism.calculateGeneFitness();
// Add to population
population.unshift(currentOrganism);
// Add to evolution history
evolutionHistory.push({
organism: currentOrganism,
timestamp: Date.now(),
event: ‘Generated’
});
displayOrganism();
updateEvolutionTree();
logMolecularEvent(`New organism ${currentOrganism.id} generated with ${currentOrganism.getAllGenes().length} genes`, ‘success’);
hideLoading();
}
function displayOrganism() {
if (!currentOrganism) return;
const container = document.getElementById(‘chromosomeDisplay’);
container.innerHTML = ”;
currentOrganism.chromosomes.forEach((chr, chrIndex) => {
const chrDiv = document.createElement(‘div’);
chrDiv.className = ‘chromosome’;
chrDiv.innerHTML = `
Genes: ${chr.genes.length} |
GC Content: ${calculateGCContent(chr.sequence)}%
Click on genes to see details
‘P‘ : ”}
${chr.regulatoryElements.find(e => e.target === gene.id && e.type === ‘enhancer’) ?
‘E‘ : ”}
${chr.regulatoryElements.find(e => e.target === gene.id && e.type === ‘silencer’) ?
‘S‘ : ”}
${gene.name} (${gene.type})
Expression: ${(gene.expression * 100).toFixed(0)}% | Conservation: ${(gene.conservationScore * 100).toFixed(0)}%
${chr.epigeneticMarks.has(gene.name) ?
`
` : ”}
`).join(”)}
`;
container.appendChild(chrDiv);
});
updatePopulationStats();
updateRegulatoryNetwork();
}
function displaySequence(sequence) {
return sequence.split(”).map(base =>
`${base}`
).join(”);
}
function calculateGCContent(sequence) {
const gcCount = (sequence.match(/[GC]/g) || []).length;
return ((gcCount / sequence.length) * 100).toFixed(1);
}
function getGeneClass(gene) {
if (gene.expression > 0.8) return ‘enhanced’;
if (gene.expression > 0.3) return ‘active’;
return ‘silenced’;
}
function showGeneDetails(geneId) {
selectedGeneId = geneId;
const gene = currentOrganism.getAllGenes().find(g => g.id === geneId);
if (!gene) return;
const detailsDiv = document.getElementById(‘geneDetails’);
detailsDiv.innerHTML = `
${gene.name}
Type: ${gene.type}
Expression Level: ${(gene.expression * 100).toFixed(1)}%
Conservation Score: ${(gene.conservationScore * 100).toFixed(1)}%
Position: ${gene.start}-${gene.end} (${gene.end – gene.start}bp)
Sequence:
Splice Variants: ${gene.spliceVariants.length}
${gene.spliceVariants.map((variant, i) => `
Efficiency: ${(variant.efficiency * 100).toFixed(0)}%
Length: ${variant.sequence.length}bp
`).join(”)}
`;
document.getElementById(‘geneModal’).style.display = ‘block’;
}
function closeModal() {
document.getElementById(‘geneModal’).style.display = ‘none’;
selectedGeneId = null;
}
function updatePopulationStats() {
if (!currentOrganism) return;
const container = document.getElementById(‘populationStats’);
const totalGenes = currentOrganism.getAllGenes().length;
const activeGenes = currentOrganism.getActiveGenes().length;
container.innerHTML = `
${currentOrganism.id}
${currentOrganism.generation}
${totalGenes}
${activeGenes}
${((activeGenes/totalGenes) * 100).toFixed(1)}%
${currentOrganism.fitness.toFixed(3)}
${environmentalPressure}
${population.length}
${evolutionHistory.length}
${currentOrganism.age} cycles
`;
}
function getEnvironmentColor(env) {
const colors = {
‘high_stress’: ‘#f44336’,
‘low_energy’: ‘#ff9800’,
‘toxic’: ‘#9c27b0’,
‘optimal’: ‘#4caf50’,
‘neutral’: ‘#90a4ae’
};
return colors[env] || ‘#90a4ae’;
}
function updateRegulatoryNetwork() {
if (!currentOrganism) return;
const container = document.getElementById(‘regulatoryNetwork’);
const allElements = currentOrganism.chromosomes.flatMap(chr => chr.regulatoryElements);
// Group elements by type
const grouped = allElements.reduce((acc, element) => {
if (!acc[element.type]) acc[element.type] = [];
acc[element.type].push(element);
return acc;
}, {});
let networkHtml = ”;
Object.entries(grouped).forEach(([type, elements]) => {
networkHtml += `
${elements.slice(0, 5).map(el => {
const strength = (el.strength * 100).toFixed(0);
const gene = currentOrganism.getAllGenes().find(g => g.id === el.target);
return `${gene ? gene.name : ‘unknown’} (${strength}%)`;
}).join(‘ ‘)}
${elements.length > 5 ? `… and ${elements.length – 5} more` : ”}
`;
});
container.innerHTML = networkHtml || ‘
No regulatory elements active
‘;
}
function getElementColor(type) {
const colors = {
‘promoter’: ‘#1976d2’,
‘enhancer’: ‘#ff9800’,
‘silencer’: ‘#f44336’,
‘insulator’: ‘#9c27b0’
};
return colors[type] || ‘#90a4ae’;
}
function updateEvolutionTree() {
const container = document.getElementById(‘evolutionTree’);
const recentHistory = evolutionHistory.slice(-20); // Show last 20 events for performance
if (recentHistory.length === 0) {
container.innerHTML = ‘
No evolutionary history yet…
‘;
return;
}
container.innerHTML = `
` + recentHistory.map(entry => {
const time = new Date(entry.timestamp).toLocaleTimeString();
return `
Gen ${entry.organism.generation} | ${entry.event}
${time}
`;
}).join(”);
}
function transcribeGenes() {
if (!currentOrganism) {
logMolecularEvent(“No organism to transcribe”, ‘error’);
return;
}
showLoading();
currentTranscripts.clear();
let transcribedCount = 0;
currentOrganism.chromosomes.forEach(chr => {
chr.genes.forEach(gene => {
// Calculate transcription rate based on all factors
let transcriptionRate = gene.expression;
// Apply regulatory effects
chr.regulatoryElements.forEach(element => {
if (element.target === gene.id) {
if (element.type === ‘enhancer’) {
const distance = Math.abs(element.position – gene.start);
const distanceEffect = element.distance_effect ? Math.exp(-distance / 1000) : 1;
transcriptionRate *= (1 + element.strength * distanceEffect * 0.5);
} else if (element.type === ‘silencer’) {
transcriptionRate *= (1 – element.strength * 0.3);
} else if (element.type === ‘promoter’) {
transcriptionRate *= (1 + element.strength * 0.2);
}
}
});
// Apply epigenetic effects
if (chr.epigeneticMarks.has(gene.name)) {
const mark = chr.epigeneticMarks.get(gene.name);
if (mark === ‘methylated’) transcriptionRate *= 0.3;
if (mark === ‘acetylated’) transcriptionRate *= 1.8;
if (mark === ‘deacetylated’) transcriptionRate *= 0.6;
}
// Environmental effects
switch (environmentalPressure) {
case ‘high_stress’:
if (gene.type === ‘regulatory’) transcriptionRate *= 1.5;
break;
case ‘low_energy’:
transcriptionRate *= 0.7;
break;
case ‘toxic’:
if (gene.type === ‘metabolic’) transcriptionRate *= 0.5;
break;
case ‘optimal’:
transcriptionRate *= 1.2;
break;
}
transcriptionRate = Math.min(1, Math.max(0, transcriptionRate));
if (transcriptionRate > 0.2) {
// Transcribe to mRNA
const mRNA = gene.sequence.replace(/T/g, ‘U’);
currentTranscripts.set(gene.id, {
sequence: mRNA,
gene: gene,
level: transcriptionRate,
spliceVariants: gene.spliceVariants.map(v => ({
…v,
sequence: v.sequence.replace(/T/g, ‘U’)
}))
});
transcribedCount++;
}
});
});
displayGeneExpression();
updateMetabolicPathway();
logMolecularEvent(`Transcribed ${transcribedCount} genes to mRNA`, ‘success’);
hideLoading();
}
function displayGeneExpression() {
const container = document.getElementById(‘geneExpression’);
if (currentTranscripts.size === 0) {
container.innerHTML = ‘
No genes being expressed…
‘;
return;
}
container.innerHTML = ”;
currentTranscripts.forEach((transcript, geneId) => {
const transcriptDiv = document.createElement(‘div’);
transcriptDiv.className = ‘transcription-unit’;
transcriptDiv.innerHTML = `
${transcript.gene.name} – Expression Level: ${(transcript.level * 100).toFixed(0)}%
Length: ${transcript.sequence.length} nucleotides
${transcript.spliceVariants.map((variant, i) => `
Efficiency: ${(variant.efficiency * 100).toFixed(0)}%
`).join(”)}
`;
container.appendChild(transcriptDiv);
});
}
function processAlternativeSplicing() {
if (currentTranscripts.size === 0) {
logMolecularEvent(“No transcripts to splice”, ‘error’);
return;
}
showLoading();
let splicingEvents = 0;
currentTranscripts.forEach((transcript, geneId) => {
// Choose splice variant based on environmental conditions and efficiency
let chosenVariant;
if (environmentalPressure === ‘high_stress’ && transcript.spliceVariants.length > 1) {
// Under stress, prefer shorter variants
chosenVariant = transcript.spliceVariants.reduce((shortest, current) =>
current.sequence.length sum + v.efficiency, 0);
let random = Math.random() * totalEfficiency;
for (const variant of transcript.spliceVariants) {
random -= variant.efficiency;
if (random {
const sequence = transcript.finalVariant ? transcript.finalVariant.sequence : transcript.sequence;
const elements = translateSequence(sequence);
const protein = {
gene: transcript.gene,
elements: elements,
level: transcript.level,
modifications: generatePostTranslationalMods(),
interactions: []
};
currentOrganism.proteins.set(geneId, protein);
// Weight elements by expression level
const weightedElements = elements.map(el => ({
element: el,
weight: transcript.level,
type: transcript.gene.type
}));
narrativeElements.push(…weightedElements);
});
// Protein-protein interactions
const proteinArray = Array.from(currentOrganism.proteins.values());
for (let i = 0; i < proteinArray.length; i++) {
for (let j = i + 1; j < proteinArray.length; j++) {
if (Math.random() < 0.3) {
proteinArray[i].interactions.push(proteinArray[j].gene.name);
proteinArray[j].interactions.push(proteinArray[i].gene.name);
proteinComplexes.push({
proteins: [proteinArray[i].gene.name, proteinArray[j].gene.name],
type: 'dimer'
});
}
}
}
// Update metabolic state
updateMetabolicState(narrativeElements);
// Generate narrative
const story = assembleNarrative(narrativeElements, proteinComplexes);
document.getElementById('storyOutput').innerHTML = story;
updateMetabolicPathway();
logMolecularEvent(`Translated ${currentTranscripts.size} transcripts to ${narrativeElements.length} narrative elements`, 'success');
hideLoading();
}
function translateSequence(sequence) {
const elements = [];
const processedCodons = new Set();
// Try all three reading frames
for (let frame = 0; frame < 3; frame++) {
for (let i = frame; i < sequence.length – 2; i += 3) {
const codon = sequence.substring(i, i + 3).replace(/U/g, 'T');
if (processedCodons.has(i + ':' + codon)) continue;
processedCodons.add(i + ':' + codon);
if (geneticCode[codon]) {
if (geneticCode[codon].stop) {
if (frame === 0) break; // Only stop on main frame
} else {
const element = geneticCode[codon].element || geneticCode[codon];
if (typeof element === 'string') {
elements.push(element);
}
}
}
}
}
return elements;
}
function generatePostTranslationalMods() {
const mods = [];
if (Math.random() < 0.3) mods.push('phosphorylated');
if (Math.random() < 0.2) mods.push('glycosylated');
if (Math.random() < 0.15) mods.push('ubiquitinated');
if (Math.random() < 0.1) mods.push('sumoylated');
if (Math.random() {
if (!elementCounts[type]) elementCounts[type] = 0;
elementCounts[type]++;
uniqueElements.add(element);
totalWeight += weight;
});
// Calculate more nuanced metabolic scores
const avgWeight = totalWeight / Math.max(1, elements.length);
// Narrative coherence – based on element diversity and weight distribution
const diversityScore = uniqueElements.size / Math.max(1, elements.length);
currentOrganism.metabolicState.narrativeCoherence = Math.min(100,
diversityScore * 80 + avgWeight * 20
);
// Character depth – based on character-related elements
const characterElements = elements.filter(e =>
e.element.includes(‘hero’) || e.element.includes(‘villain’) ||
e.element.includes(‘mentor’) || e.element.includes(‘character’)
).length;
const characterTransformations = elements.filter(e =>
e.element.includes(‘transform’) || e.element.includes(‘realizes’) ||
e.element.includes(‘awakens’)
).length;
currentOrganism.metabolicState.characterDepth = Math.min(100,
characterElements * 8 + characterTransformations * 12
);
// Plot complexity – based on actions, conflicts, and revelations
const plotElements = elements.filter(e =>
e.element.includes(‘betrayal’) || e.element.includes(‘reveals’) ||
e.element.includes(‘mystery’) || e.element.includes(‘conflict’)
).length;
const actionElements = elements.filter(e =>
e.element.includes(‘strikes’) || e.element.includes(‘searches’) ||
e.element.includes(‘discovers’) || e.element.includes(‘threatens’)
).length;
currentOrganism.metabolicState.plotComplexity = Math.min(100,
plotElements * 15 + actionElements * 5
);
// Thematic resonance – based on emotional and environmental elements
const emotionalElements = elements.filter(e =>
e.element.includes(‘love’) || e.element.includes(‘fear’) ||
e.element.includes(‘hope’) || e.element.includes(‘passion’)
).length;
const environmentalElements = elements.filter(e =>
e.element.includes(‘forest’) || e.element.includes(‘temple’) ||
e.element.includes(‘sky’) || e.element.includes(‘realm’)
).length;
currentOrganism.metabolicState.thematicResonance = Math.min(100,
emotionalElements * 12 + environmentalElements * 8
);
// Ensure minimum values
Object.keys(currentOrganism.metabolicState).forEach(key => {
if (key !== ‘energy’ && currentOrganism.metabolicState[key] < 5) {
currentOrganism.metabolicState[key] = 5;
}
});
// Recalculate fitness
currentOrganism.calculateGeneFitness();
}
function assembleNarrative(elements, proteinComplexes) {
if (elements.length === 0) return "
No narrative elements generated.
“;
// Create a narrative context to track story state
const context = {
usedElements: new Set(),
characters: new Set(),
locations: new Set(),
currentMood: null,
plotPoints: [],
lastAction: null
};
// Sort and deduplicate elements
const uniqueElements = [];
const elementMap = new Map();
elements.forEach(({element, weight, type}) => {
if (!elementMap.has(element)) {
elementMap.set(element, {element, weight, type, count: 1});
} else {
elementMap.get(element).count++;
elementMap.get(element).weight += weight;
}
});
// Convert to array and sort by combined weight
uniqueElements.push(…Array.from(elementMap.values()));
uniqueElements.sort((a, b) => (b.weight * b.count) – (a.weight * a.count));
// Classify elements into narrative categories
const classified = classifyNarrativeElements(uniqueElements);
// Determine narrative arc based on available elements
const narrativeArc = determineNarrativeArc(classified);
// Build the story
let story = ”;
// Title
const chapterTitle = generateDynamicTitle(narrativeArc, classified, currentOrganism.generation);
story += `
`;
// Opening paragraph – establish setting and initial state
story += ‘
‘;
story += generateOpening(classified, context);
story += ‘
‘;
// Development paragraphs – build the narrative
const developmentParagraphs = generateDevelopment(classified, context, narrativeArc);
developmentParagraphs.forEach(para => {
story += `
${para}
`;
});
// Climax paragraph
if (classified.conflicts.length > 0 || classified.transformations.length > 0) {
story += ‘
‘;
story += generateClimax(classified, context);
story += ‘
‘;
}
// Resolution
story += ‘
‘;
story += generateResolution(classified, context, proteinComplexes);
story += ‘
‘;
// Apply environmental effects more subtly
story = applyNuancedEnvironmentalEffects(story);
// Calculate proper metabolic scores based on actual narrative quality
updateNarrativeMetabolism(classified, context);
// Add metadata
story += `
Narrative coherence: ${currentOrganism.metabolicState.narrativeCoherence.toFixed(0)}% |
Character depth: ${currentOrganism.metabolicState.characterDepth.toFixed(0)}% |
Plot complexity: ${currentOrganism.metabolicState.plotComplexity.toFixed(0)}% |
Thematic resonance: ${currentOrganism.metabolicState.thematicResonance.toFixed(0)}%
`;
return story;
}
function classifyNarrativeElements(elements) {
const classified = {
characters: [],
actions: [],
locations: [],
emotions: [],
conflicts: [],
transformations: [],
revelations: [],
relationships: []
};
elements.forEach(item => {
const element = item.element;
if (element.includes(‘hero_’) || element.includes(‘villain_’) || element.includes(‘mentor’) ||
element.includes(‘guardian’) || element.includes(‘stranger’) || element.includes(‘child’)) {
if (element.includes(‘_’) && !element.includes(‘appears’) && !element.includes(‘guides’)) {
const parts = element.split(‘_’);
if (parts.length >= 2) {
classified.characters.push({…item, character: parts[0], action: parts.slice(1).join(‘_’)});
}
} else {
classified.relationships.push(item);
}
}
if (element.includes(‘awakens’) || element.includes(‘strikes’) || element.includes(‘searches’) ||
element.includes(‘discovers’) || element.includes(‘threatens’) || element.includes(‘gathers’)) {
classified.actions.push(item);
}
if (element.includes(‘forest’) || element.includes(‘library’) || element.includes(‘temple’) ||
element.includes(‘mountain’) || element.includes(‘desert’) || element.includes(‘city’) ||
element.includes(‘cavern’) || element.includes(‘valley’)) {
classified.locations.push(item);
}
if (element.includes(‘love’) || element.includes(‘fear’) || element.includes(‘hope’) ||
element.includes(‘hatred’) || element.includes(‘desire’) || element.includes(‘passion’)) {
classified.emotions.push(item);
}
if (element.includes(‘betrayal’) || element.includes(‘conflict’) || element.includes(‘threatens’) ||
element.includes(‘darkness’) || element.includes(‘falls’)) {
classified.conflicts.push(item);
}
if (element.includes(‘transform’) || element.includes(‘realizes’) || element.includes(‘becomes’) ||
element.includes(‘awakens’) || element.includes(‘discovers’)) {
classified.transformations.push(item);
}
if (element.includes(‘reveals’) || element.includes(‘truth’) || element.includes(‘mystery’) ||
element.includes(‘secret’)) {
classified.revelations.push(item);
}
});
return classified;
}
function determineNarrativeArc(classified) {
const scores = {
heroic: classified.characters.filter(c => c.character === ‘hero’).length * 2 +
classified.transformations.length,
tragic: classified.conflicts.length * 2 +
classified.emotions.filter(e => e.element.includes(‘fear’) || e.element.includes(‘hatred’)).length,
mystery: classified.revelations.length * 3 +
classified.locations.filter(l => l.element.includes(‘hidden’) || l.element.includes(‘secret’)).length,
romantic: classified.emotions.filter(e => e.element.includes(‘love’) || e.element.includes(‘passion’)).length * 3,
epic: classified.locations.length + classified.characters.length + classified.conflicts.length
};
let maxScore = 0;
let selectedArc = ‘balanced’;
Object.entries(scores).forEach(([arc, score]) => {
if (score > maxScore) {
maxScore = score;
selectedArc = arc;
}
});
return selectedArc;
}
function generateDynamicTitle(arc, classified, generation) {
const arcTitles = {
heroic: [‘The Rising Dawn’, ‘Awakening Power’, ‘The Hero\’s Call’, ‘Courage Unbound’],
tragic: [‘Shadows Fall’, ‘The Price of Pride’, ‘Darkness Ascending’, ‘Fallen Stars’],
mystery: [‘Veiled Truths’, ‘The Hidden Path’, ‘Secrets Unveiled’, ‘Through the Mist’],
romantic: [‘Hearts Entwined’, ‘Love\’s Journey’, ‘Passion\’s Fire’, ‘United Souls’],
epic: [‘The Great Convergence’, ‘Worlds Collide’, ‘The Final Stand’, ‘Destiny\’s March’],
balanced: [‘The Turning’, ‘Crossroads’, ‘The Awakening’, ‘New Horizons’]
};
const titles = arcTitles[arc] || arcTitles.balanced;
const baseTitle = titles[generation % titles.length];
// Add location flavor if available
if (classified.locations.length > 0) {
const location = classified.locations[0].element;
if (location.includes(‘forest’)) return `${baseTitle} in the Woods`;
if (location.includes(‘mountain’)) return `${baseTitle} Beyond the Peaks`;
if (location.includes(‘temple’)) return `${baseTitle} of the Sacred`;
}
return `Chapter ${generation}: ${baseTitle}`;
}
function generateOpening(classified, context) {
let opening = ”;
// Set location first
if (classified.locations.length > 0) {
const location = selectBestElement(classified.locations, context);
opening += formatLocation(location.element) + ‘, ‘;
context.locations.add(location.element);
context.usedElements.add(location.element);
}
// Introduce initial character or situation
if (classified.characters.length > 0) {
const charAction = selectBestElement(classified.characters, context);
opening += formatCharacterAction(charAction) + ‘. ‘;
context.characters.add(charAction.character);
context.usedElements.add(charAction.element);
context.lastAction = charAction.action;
} else if (classified.emotions.length > 0) {
const emotion = selectBestElement(classified.emotions, context);
opening += ‘The air itself seemed to ‘ + formatElement(emotion.element) + ‘. ‘;
context.currentMood = emotion.element;
context.usedElements.add(emotion.element);
}
// Add atmospheric detail
if (classified.revelations.length > 0 && Math.random() < 0.5) {
const revelation = selectBestElement(classified.revelations, context);
opening += formatElement(revelation.element) + ', casting new light on ancient mysteries. ';
context.usedElements.add(revelation.element);
}
return opening || 'In a world between dreams and waking, the story unfolds. ';
}
function generateDevelopment(classified, context, arc) {
const paragraphs = [];
const targetParagraphs = Math.min(3, Math.floor(classified.actions.length / 2) + 1);
for (let i = 0; i !context.usedElements.has(a.element));
if (unusedActions.length > 0) {
const numActions = Math.min(3, unusedActions.length);
const selectedActions = selectMultipleElements(unusedActions, numActions, context);
if (selectedActions.length > 0) {
paragraph += buildActionSequence(selectedActions, context);
}
}
// Add character interactions
const unusedRelationships = classified.relationships.filter(r => !context.usedElements.has(r.element));
if (unusedRelationships.length > 0 && Math.random() !context.usedElements.has(e.element));
if (unusedEmotions.length > 0 && Math.random() !context.usedElements.has(c.element));
if (unusedConflicts.length > 0) {
const conflict = selectBestElement(unusedConflicts, context);
climax += ‘In a moment of desperate clarity, ‘ + formatElement(conflict.element) + ‘. ‘;
context.usedElements.add(conflict.element);
}
// Add transformation
const unusedTransformations = classified.transformations.filter(t => !context.usedElements.has(t.element));
if (unusedTransformations.length > 0) {
const transformation = selectBestElement(unusedTransformations, context);
climax += formatElement(transformation.element) + ‘, forever changing the course of fate. ‘;
context.usedElements.add(transformation.element);
}
// Final revelation
const unusedRevelations = classified.revelations.filter(r => !context.usedElements.has(r.element));
if (unusedRevelations.length > 0) {
const revelation = selectBestElement(unusedRevelations, context);
climax += ‘And in that instant, ‘ + formatElement(revelation.element) + ‘.’;
context.usedElements.add(revelation.element);
}
return climax || ‘The moment of truth arrived with thunderous intensity.’;
}
function generateResolution(classified, context, proteinComplexes) {
let resolution = ”;
// Resolve character arcs
if (context.characters.size > 0) {
const mainCharacter = Array.from(context.characters)[0];
resolution += `As the dust settled, the ${mainCharacter} `;
const unusedTransformations = classified.transformations.filter(t => !context.usedElements.has(t.element));
if (unusedTransformations.length > 0) {
const transformation = selectBestElement(unusedTransformations, context);
resolution += formatElement(transformation.element) + ‘. ‘;
} else {
resolution += ‘stood changed by all that had transpired. ‘;
}
}
// Add protein complex interactions as metaphor
if (proteinComplexes.length > 0) {
const complex = proteinComplexes[0];
resolution += `Like ${complex.proteins[0]} binding with ${complex.proteins[1]}, `;
resolution += ‘disparate elements united into something greater. ‘;
}
// Final mood
const allEmotions = classified.emotions.filter(e => !context.usedElements.has(e.element));
if (allEmotions.length > 0) {
const finalMood = selectBestElement(allEmotions, context);
resolution += ‘The story ends with ‘ + formatElement(finalMood.element) + ‘, ‘;
resolution += ‘echoing through eternity.’;
} else {
resolution += ‘And so the tale reaches its inevitable conclusion, ‘;
resolution += ‘leaving echoes that will resonate through time.’;
}
return resolution;
}
function selectBestElement(elements, context) {
// Avoid repetition by filtering out used elements
const available = elements.filter(e => !context.usedElements.has(e.element));
if (available.length === 0) return elements[0]; // Fallback
// Weight by expression level and novelty
return available.reduce((best, current) =>
current.weight > best.weight ? current : best
);
}
function selectMultipleElements(elements, count, context) {
const selected = [];
const available = […elements];
for (let i = 0; i 0; i++) {
const element = selectBestElement(available, context);
selected.push(element);
context.usedElements.add(element.element);
available.splice(available.indexOf(element), 1);
}
return selected;
}
function buildActionSequence(actions, context) {
if (actions.length === 0) return ”;
let sequence = formatElement(actions[0].element);
if (actions.length === 1) {
return sequence + ‘.’;
} else if (actions.length === 2) {
return sequence + ‘, and then ‘ + formatElement(actions[1].element) + ‘.’;
} else {
for (let i = 1; i c.toLowerCase());
}
function updateNarrativeMetabolism(classified, context) {
if (!currentOrganism) return;
// Reset scores
let coherence = 0;
let depth = 0;
let complexity = 0;
let resonance = 0;
// Narrative coherence – based on logical flow and used elements
const totalElements = Object.values(classified).flat().length;
const usedRatio = context.usedElements.size / Math.max(1, totalElements);
coherence = Math.min(100, usedRatio * 100 * 1.2);
// Character depth – based on character actions and transformations
const characterActions = classified.characters.length;
const transformations = classified.transformations.length;
depth = Math.min(100, (characterActions * 15 + transformations * 20));
// Plot complexity – based on conflicts, revelations, and relationships
const conflicts = classified.conflicts.length;
const revelations = classified.revelations.length;
const relationships = classified.relationships.length;
complexity = Math.min(100, (conflicts * 20 + revelations * 15 + relationships * 10));
// Thematic resonance – based on emotions and locations
const emotions = classified.emotions.length;
const locations = classified.locations.length;
resonance = Math.min(100, (emotions * 20 + locations * 10));
// Update organism’s metabolic state
currentOrganism.metabolicState.narrativeCoherence = coherence;
currentOrganism.metabolicState.characterDepth = depth;
currentOrganism.metabolicState.plotComplexity = complexity;
currentOrganism.metabolicState.thematicResonance = resonance;
}
function applyNuancedEnvironmentalEffects(narrative) {
switch (environmentalPressure) {
case ‘high_stress’:
return narrative
.replace(/stood/g, ‘trembled’)
.replace(/walked/g, ‘rushed’)
.replace(/said/g, ‘shouted’)
.replace(/moment/g, ‘instant’)
.replace(/slowly/g, ‘frantically’);
case ‘low_energy’:
return narrative
.replace(/rushed/g, ‘crawled’)
.replace(/exploded/g, ‘faded’)
.replace(/shouted/g, ‘whispered’)
.replace(/blazing/g, ‘dim’)
.replace(/powerful/g, ‘weak’);
case ‘toxic’:
return narrative
.replace(/pure/g, ‘corrupted’)
.replace(/clear/g, ‘murky’)
.replace(/bright/g, ‘sickly’)
.replace(/healing/g, ‘poisoning’)
.replace(/growth/g, ‘decay’);
case ‘optimal’:
return narrative
.replace(/struggled/g, ‘flourished’)
.replace(/darkness/g, ‘radiance’)
.replace(/fear/g, ‘confidence’)
.replace(/weak/g, ‘strong’)
.replace(/failed/g, ‘succeeded’);
default:
return narrative;
}
}
function updateMetabolicPathway() {
const container = document.getElementById(‘pathwayDiagram’);
container.innerHTML = ”;
const pathwaySteps = [
{ name: ‘Transcription’, active: currentTranscripts.size > 0 },
{ name: ‘Splicing’, active: Array.from(currentTranscripts.values()).some(t => t.finalVariant) },
{ name: ‘Translation’, active: currentOrganism && currentOrganism.proteins.size > 0 },
{ name: ‘Folding’, active: currentOrganism && currentOrganism.proteins.size > 0 },
{ name: ‘Assembly’, active: false }, // Would need protein complex detection
{ name: ‘Expression’, active: document.getElementById(‘storyOutput’).innerHTML.includes(‘Chapter’) }
];
pathwaySteps.forEach((step, index) => {
const stepDiv = document.createElement(‘div’);
stepDiv.className = `pathway-step ${step.active ? ‘active’ : ”}`;
const efficiency = step.active ? 50 + Math.floor(Math.random() * 50) : 0;
stepDiv.innerHTML = `
${step.name}
${efficiency}% efficiency
${step.active ? ‘
✓ Active‘ : ”}
`;
container.appendChild(stepDiv);
});
}
function introduceMutations() {
if (!currentOrganism) {
logMolecularEvent(“No organism to mutate”, ‘error’);
return;
}
showLoading();
let mutationCount = 0;
const mutationTypes = [];
currentOrganism.chromosomes.forEach(chr => {
// Point mutations
const numPointMutations = Math.floor(Math.random() * 5);
for (let i = 0; i < numPointMutations; i++) {
if (Math.random() b !== oldBase)[Math.floor(Math.random() * 3)];
chr.sequence = chr.sequence.substring(0, position) + newBase +
chr.sequence.substring(position + 1);
mutationCount++;
mutationTypes.push(`Point mutation: ${oldBase}→${newBase} at ${chr.id}:${position}`);
}
}
// Insertions/Deletions
if (Math.random() < 0.1) {
const position = Math.floor(Math.random() * chr.sequence.length);
if (Math.random() 100) {
const deletedBase = chr.sequence[position];
chr.sequence = chr.sequence.substring(0, position) +
chr.sequence.substring(position + 1);
mutationTypes.push(`Deletion: -${deletedBase} at ${chr.id}:${position}`);
}
}
mutationCount++;
}
// Re-identify genes after sequence mutations
chr.genes = chr.identifyGenes();
// Expression mutations
chr.genes.forEach(gene => {
if (Math.random() 0.1) {
mutationTypes.push(`Expression change in ${gene.name}: ${(change > 0 ? ‘+’ : ”)}${(change * 100).toFixed(0)}%`);
mutationCount++;
}
}
});
// Regulatory element mutations
chr.regulatoryElements.forEach(element => {
if (Math.random() {
logMolecularEvent(mut, ‘warning’);
});
if (mutationTypes.length > 5) {
logMolecularEvent(`… and ${mutationTypes.length – 5} more mutations`, ‘warning’);
}
logMolecularEvent(`Introduced ${mutationCount} mutations total`, ‘success’);
hideLoading();
}
function modifyEpigenetics() {
if (!currentOrganism) {
logMolecularEvent(“No organism for epigenetic modification”, ‘error’);
return;
}
showLoading();
let modificationsCount = 0;
const modifications = [];
currentOrganism.chromosomes.forEach(chr => {
chr.genes.forEach(gene => {
if (Math.random() < 0.3) {
const marks = ['methylated', 'acetylated', 'deacetylated'];
const mark = marks[Math.floor(Math.random() * marks.length)];
const previousMark = chr.epigeneticMarks.get(gene.name);
chr.epigeneticMarks.set(gene.name, mark);
modificationsCount++;
modifications.push(`${gene.name}: ${previousMark || 'none'} → ${mark}`);
// Add histone modifications
if (Math.random() < 0.5) {
const histoneMods = ['H3K4me3', 'H3K9me3', 'H3K27me3', 'H3K36me3'];
const mod = histoneMods[Math.floor(Math.random() * histoneMods.length)];
chr.addHistoneModification(gene.start, mod);
}
}
});
// DNA methylation patterns
if (Math.random() {
logMolecularEvent(`Epigenetic: ${mod}`, ‘success’);
});
logMolecularEvent(`Applied ${modificationsCount} epigenetic modifications`, ‘success’);
hideLoading();
}
function simulateEnvironment() {
const environments = [‘high_stress’, ‘low_energy’, ‘toxic’, ‘optimal’, ‘neutral’];
const previousEnvironment = environmentalPressure;
environmentalPressure = environments[Math.floor(Math.random() * environments.length)];
logMolecularEvent(`Environmental shift: ${previousEnvironment} → ${environmentalPressure}`, ‘warning’);
// Environmental effects on gene expression
if (currentOrganism) {
currentOrganism.chromosomes.forEach(chr => {
chr.genes.forEach(gene => {
let expressionChange = 0;
switch (environmentalPressure) {
case ‘high_stress’:
if (gene.type === ‘regulatory’) expressionChange = 0.3;
if (gene.type === ‘metabolic’) expressionChange = 0.2;
break;
case ‘low_energy’:
expressionChange = -0.2;
break;
case ‘toxic’:
if (gene.type === ‘structural’) expressionChange = -0.4;
if (gene.type === ‘metabolic’) expressionChange = -0.3;
break;
case ‘optimal’:
expressionChange = 0.2;
break;
}
gene.expression = Math.max(0, Math.min(1, gene.expression + expressionChange));
});
});
currentOrganism.calculateGeneFitness();
displayOrganism();
}
updatePopulationStats();
}
function sexualReproduction() {
if (!currentOrganism) {
logMolecularEvent(“No organism for reproduction”, ‘error’);
return;
}
if (population.length b.fitness – a.fitness);
const parent1 = currentOrganism;
const parent2 = sortedPop.find(org => org.id !== parent1.id) || sortedPop[1];
logMolecularEvent(`Sexual reproduction: ${parent1.id} × ${parent2.id}`, ‘success’);
// Create offspring
const offspring = new Organism(parent1, parent2);
// Apply some mutations to offspring
offspring.mutationRate = (parent1.mutationRate + parent2.mutationRate) / 2 *
(0.8 + Math.random() * 0.4);
// Small chance of beneficial mutation
if (Math.random() {
const luckyGene = chr.genes[Math.floor(Math.random() * chr.genes.length)];
if (luckyGene) {
luckyGene.expression = Math.min(1, luckyGene.expression * 1.5);
logMolecularEvent(`Beneficial mutation in offspring: ${luckyGene.name} enhanced`, ‘success’);
}
});
}
offspring.calculateGeneFitness();
// Update reproductive success
parent1.reproductiveSuccess++;
parent2.reproductiveSuccess++;
// Add to population
population.unshift(offspring);
// Add to evolution history
evolutionHistory.push({
organism: offspring,
timestamp: Date.now(),
event: `Born from ${parent1.id} × ${parent2.id}`
});
currentOrganism = offspring;
displayOrganism();
updateEvolutionTree();
logMolecularEvent(`Offspring ${offspring.id} created (Generation ${offspring.generation}, Fitness: ${offspring.fitness.toFixed(3)})`, ‘success’);
hideLoading();
}
function horizontalGeneTransfer() {
if (!currentOrganism || population.length org.id !== currentOrganism.id);
if (!donor) {
logMolecularEvent(“No suitable donor for horizontal transfer”, ‘error’);
hideLoading();
return;
}
// Select random gene from donor
const donorGenes = donor.getAllGenes();
const transferredGene = donorGenes[Math.floor(Math.random() * donorGenes.length)];
// Select random chromosome in recipient
const targetChromosome = currentOrganism.chromosomes[
Math.floor(Math.random() * currentOrganism.chromosomes.length)
];
// Insert gene sequence
const insertionPoint = Math.floor(Math.random() * targetChromosome.sequence.length);
targetChromosome.sequence =
targetChromosome.sequence.substring(0, insertionPoint) +
transferredGene.sequence +
targetChromosome.sequence.substring(insertionPoint);
// Re-identify genes
targetChromosome.genes = targetChromosome.identifyGenes();
// Recalculate fitness
currentOrganism.calculateGeneFitness();
logMolecularEvent(`Horizontal transfer: ${transferredGene.name} from ${donor.id} → ${currentOrganism.id}`, ‘success’);
displayOrganism();
hideLoading();
}
function runEvolution(generations) {
if (!currentOrganism) {
generateOrganism();
}
showLoading();
logMolecularEvent(`Starting evolution simulation for ${generations} generations…`, ‘success’);
let generationCount = 0;
function evolveGeneration() {
if (generationCount >= generations) {
// Final summary
const fittest = population.reduce((best, current) =>
current.fitness > best.fitness ? current : best
);
logMolecularEvent(`Evolution complete! Fittest organism: ${fittest.id} (Fitness: ${fittest.fitness.toFixed(3)})`, ‘success’);
currentOrganism = fittest;
displayOrganism();
updateEvolutionTree();
hideLoading();
return;
}
// Simulate one generation
simulateEnvironment();
// Reproduction for top organisms
const breedingPop = […population]
.sort((a, b) => b.fitness – a.fitness)
.slice(0, Math.min(population.length, Math.ceil(population.length / 2)));
// Limit breeding pairs for performance
const maxBreedingPairs = Math.min(10, Math.floor(breedingPop.length / 2));
for (let i = 0; i < maxBreedingPairs * 2 && i {
for (let j = 0; j < 3; j++) {
if (Math.random() 100) {
// Only cull if population gets very large to maintain performance
population.sort((a, b) => b.fitness – a.fitness);
population = population.slice(0, Math.floor(population.length * 0.8)); // Keep top 80%
}
// Age all organisms
population.forEach(org => org.ageOrganism());
generationCount++;
logMolecularEvent(`Generation ${generationCount}/${generations} complete. Population fitness: ${
(population.reduce((sum, org) => sum + org.fitness, 0) / population.length).toFixed(3)
}`, ‘success’);
// Continue evolution
setTimeout(evolveGeneration, 100);
}
evolveGeneration();
}
function analyzeGenome() {
if (!currentOrganism) {
logMolecularEvent(“No organism to analyze”, ‘error’);
return;
}
showLoading();
const analysis = {
totalGenes: currentOrganism.getAllGenes().length,
activeGenes: currentOrganism.getActiveGenes().length,
genomeSize: currentOrganism.chromosomes.reduce((sum, chr) => sum + chr.sequence.length, 0),
geneTypes: {},
averageGeneLength: 0,
gcContent: 0,
epigeneticMarks: 0,
regulatoryElements: 0
};
// Gene type distribution
currentOrganism.getAllGenes().forEach(gene => {
if (!analysis.geneTypes[gene.type]) {
analysis.geneTypes[gene.type] = 0;
}
analysis.geneTypes[gene.type]++;
analysis.averageGeneLength += gene.sequence.length;
});
analysis.averageGeneLength /= analysis.totalGenes;
// Calculate genome-wide GC content
let totalGC = 0;
let totalBases = 0;
currentOrganism.chromosomes.forEach(chr => {
const gc = (chr.sequence.match(/[GC]/g) || []).length;
totalGC += gc;
totalBases += chr.sequence.length;
// Count epigenetic marks
analysis.epigeneticMarks += chr.epigeneticMarks.size;
// Count regulatory elements
analysis.regulatoryElements += chr.regulatoryElements.length;
});
analysis.gcContent = (totalGC / totalBases * 100).toFixed(1);
// Generate analysis report
const reportHtml = `
Genome Analysis Report
Organism ID: ${currentOrganism.id}
Generation: ${currentOrganism.generation}
Fitness Score: ${currentOrganism.fitness.toFixed(3)}
Genome Statistics
Genome Size: ${analysis.genomeSize} bp
Total Genes: ${analysis.totalGenes}
Active Genes: ${analysis.activeGenes} (${(analysis.activeGenes/analysis.totalGenes*100).toFixed(1)}%)
Average Gene Length: ${analysis.averageGeneLength.toFixed(0)} bp
GC Content: ${analysis.gcContent}%
Gene Type Distribution
${Object.entries(analysis.geneTypes).map(([type, count]) =>
`
${type}: ${count} genes (${(count/analysis.totalGenes*100).toFixed(1)}%)
`
).join(”)}
Regulatory Features
Regulatory Elements: ${analysis.regulatoryElements}
Epigenetic Marks: ${analysis.epigeneticMarks}
Metabolic State
Narrative Coherence: ${currentOrganism.metabolicState.narrativeCoherence.toFixed(0)}%
Character Depth: ${currentOrganism.metabolicState.characterDepth.toFixed(0)}%
Plot Complexity: ${currentOrganism.metabolicState.plotComplexity.toFixed(0)}%
Thematic Resonance: ${currentOrganism.metabolicState.thematicResonance.toFixed(0)}%
`;
// Show analysis in modal
document.getElementById(‘geneDetails’).innerHTML = reportHtml;
document.getElementById(‘geneModal’).style.display = ‘block’;
logMolecularEvent(“Genome analysis complete”, ‘success’);
hideLoading();
}
function fullLifeCycle() {
if (!currentOrganism) {
generateOrganism();
}
logMolecularEvent(“Initiating complete life cycle…”, ‘success’);
const steps = [
{ fn: transcribeGenes, delay: 300 },
{ fn: processAlternativeSplicing, delay: 600 },
{ fn: expressProteins, delay: 900 },
{ fn: introduceMutations, delay: 1200 },
{ fn: simulateEnvironment, delay: 1500 },
{ fn: () => {
if (population.length >= 2 && Math.random() < 0.7) {
sexualReproduction();
} else if (Math.random() {
setTimeout(step.fn, step.delay);
});
setTimeout(() => {
logMolecularEvent(“Life cycle complete!”, ‘success’);
}, 2100);
}
function saveOrganism() {
if (!currentOrganism) {
logMolecularEvent(“No organism to save”, ‘error’);
return;
}
const saveData = {
organism: {
id: currentOrganism.id,
generation: currentOrganism.generation,
chromosomes: currentOrganism.chromosomes.map(chr => ({
id: chr.id,
sequence: chr.sequence,
epigeneticMarks: Array.from(chr.epigeneticMarks.entries())
})),
fitness: currentOrganism.fitness,
metabolicState: currentOrganism.metabolicState,
mutationRate: currentOrganism.mutationRate,
age: currentOrganism.age
},
environment: environmentalPressure,
timestamp: Date.now()
};
const dataStr = JSON.stringify(saveData);
const dataBlob = new Blob([dataStr], {type: ‘application/json’});
const url = URL.createObjectURL(dataBlob);
const link = document.createElement(‘a’);
link.href = url;
link.download = `organism_${currentOrganism.id}_gen${currentOrganism.generation}.json`;
link.click();
logMolecularEvent(`Organism ${currentOrganism.id} saved to file`, ‘success’);
}
function loadOrganism() {
const input = document.createElement(‘input’);
input.type = ‘file’;
input.accept = ‘.json’;
input.onchange = (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
try {
const saveData = JSON.parse(event.target.result);
// Reconstruct organism
currentOrganism = new Organism();
currentOrganism.id = saveData.organism.id;
currentOrganism.generation = saveData.organism.generation;
currentOrganism.fitness = saveData.organism.fitness;
currentOrganism.metabolicState = saveData.organism.metabolicState;
currentOrganism.mutationRate = saveData.organism.mutationRate;
currentOrganism.age = saveData.organism.age;
// Reconstruct chromosomes
currentOrganism.chromosomes = saveData.organism.chromosomes.map(chrData => {
const chr = new Chromosome(chrData.id, 0, chrData.sequence);
chrData.epigeneticMarks.forEach(([key, value]) => {
chr.epigeneticMarks.set(key, value);
});
return chr;
});
environmentalPressure = saveData.environment;
// Add to population
population.unshift(currentOrganism);
displayOrganism();
updateEvolutionTree();
logMolecularEvent(`Organism ${currentOrganism.id} loaded successfully`, ‘success’);
} catch (error) {
logMolecularEvent(`Error loading organism: ${error.message}`, ‘error’);
}
};
reader.readAsText(file);
};
input.click();
}
function logMolecularEvent(message, type = ‘info’) {
const log = document.getElementById(‘molecularLog’);
const time = new Date().toLocaleTimeString();
const entry = document.createElement(‘div’);
entry.className = `log-entry ${type}`;
entry.innerHTML = `[${time}] ${message}`;
log.appendChild(entry);
log.scrollTop = log.scrollHeight;
// Keep log size manageable
while (log.children.length > 100) {
log.removeChild(log.firstChild);
}
}
// Event listeners
window.onclick = function(event) {
if (event.target == document.getElementById(‘geneModal’)) {
closeModal();
}
}
// Keyboard shortcuts
document.addEventListener(‘keydown’, (e) => {
if (e.ctrlKey || e.metaKey) {
switch (e.key) {
case ‘g’:
e.preventDefault();
generateOrganism();
break;
case ‘t’:
e.preventDefault();
transcribeGenes();
break;
case ‘e’:
e.preventDefault();
expressProteins();
break;
case ‘m’:
e.preventDefault();
introduceMutations();
break;
case ‘s’:
e.preventDefault();
saveOrganism();
break;
case ‘l’:
e.preventDefault();
loadOrganism();
break;
}
}
});
// Initialize
window.onload = function() {
logMolecularEvent(“Advanced Molecular Narrative Engine v3.0 initialized”, ‘success’);
logMolecularEvent(“Ready for organism generation and genetic processes”, ‘info’);
logMolecularEvent(“Keyboard shortcuts: Ctrl+G (Generate), Ctrl+T (Transcribe), Ctrl+E (Express), Ctrl+M (Mutate)”, ‘info’);
logMolecularEvent(“Population size is unlimited – large populations may impact performance”, ‘warning’);
// Display initial empty state
updatePopulationStats();
updateRegulatoryNetwork();
updateEvolutionTree();
updateMetabolicPathway();
};