// Educational browser model source snapshot. Not the native oNeura engine or a validated clinical model. // Generated from the inline script in ddi.html. No patient decisions. // ─── DRUG DATABASE ─── const DRUGS = [ { id:'warfarin', name:'Warfarin', mw:308, logp:3.0, proteinBinding:0.99, cypSubstrates:['CYP2C9'], cypInhibitors:[], pgpSubstrate:false, pgpInhibitor:false, halfLife:40, tmax:4, bioavailability:0.80, therapeuticIndex:'narrow', // narrow TI organEffects:['Anticoagulation','Bleeding risk'], clearance:0.17, // L/h/kg scaled concept dose_mg:5, color:'#c25a3f', // PD: effect on INR / coagulation receptorAffinity:0.95, // VKORC1 inhibition affectedSystems:['coagulation'] }, { id:'fluconazole', name:'Fluconazole', mw:306, logp:0.5, proteinBinding:0.12, cypSubstrates:[], cypInhibitors:['CYP2C9','CYP3A4'], pgpSubstrate:false, pgpInhibitor:false, halfLife:30, tmax:2, bioavailability:0.90, therapeuticIndex:'wide', organEffects:['Antifungal','Hepatotoxicity'], clearance:0.23, dose_mg:200, color:'#b78c63', receptorAffinity:0.7, affectedSystems:['fungal'] }, { id:'phenytoin', name:'Phenytoin', mw:252, logp:2.5, proteinBinding:0.90, cypSubstrates:['CYP2C9'], cypInhibitors:[], pgpSubstrate:false, pgpInhibitor:false, halfLife:22, tmax:6, bioavailability:0.85, therapeuticIndex:'narrow', organEffects:['Seizure control','CNS depression','Gingival hyperplasia'], clearance:0.18, dose_mg:300, color:'#8a9b5e', receptorAffinity:0.85, // Na+ channel block affectedSystems:['cns'] }, { id:'metoprolol', name:'Metoprolol', mw:267, logp:1.9, proteinBinding:0.12, cypSubstrates:['CYP2D6'], cypInhibitors:[], pgpSubstrate:false, pgpInhibitor:false, halfLife:3.5, tmax:1.5, bioavailability:0.50, therapeuticIndex:'moderate', organEffects:['Heart rate reduction','BP lowering','Bronchospasm'], clearance:0.75, dose_mg:100, color:'#a8784a', receptorAffinity:0.80, // beta-1 blockade affectedSystems:['cardiovascular'] }, { id:'amiodarone', name:'Amiodarone', mw:645, logp:7.6, proteinBinding:0.96, cypSubstrates:[], cypInhibitors:['CYP3A4','CYP2C9'], pgpSubstrate:false, pgpInhibitor:true, halfLife:55, tmax:5, bioavailability:0.45, therapeuticIndex:'narrow', organEffects:['QT prolongation','Thyroid dysfunction','Pulmonary toxicity'], clearance:0.12, dose_mg:200, color:'#c2603f', receptorAffinity:0.90, // multi-channel block affectedSystems:['cardiovascular','thyroid'] }, { id:'simvastatin', name:'Simvastatin', mw:419, logp:4.7, proteinBinding:0.95, cypSubstrates:['CYP3A4'], cypInhibitors:[], pgpSubstrate:false, pgpInhibitor:false, halfLife:2.8, tmax:1.5, bioavailability:0.05, therapeuticIndex:'moderate', organEffects:['Cholesterol reduction','Myopathy','Rhabdomyolysis'], clearance:0.85, dose_mg:40, color:'#e09a4f', receptorAffinity:0.75, // HMG-CoA reductase affectedSystems:['musculoskeletal','hepatic'] }, { id:'clarithromycin', name:'Clarithromycin', mw:748, logp:3.2, proteinBinding:0.72, cypSubstrates:[], cypInhibitors:['CYP3A4'], pgpSubstrate:false, pgpInhibitor:true, halfLife:5, tmax:2.5, bioavailability:0.55, therapeuticIndex:'wide', organEffects:['Antibacterial','QT prolongation','GI disturbance'], clearance:0.50, dose_mg:500, color:'#ad6155', receptorAffinity:0.6, affectedSystems:['cardiovascular','gi'] }, { id:'digoxin', name:'Digoxin', mw:781, logp:1.3, proteinBinding:0.25, cypSubstrates:[], cypInhibitors:[], pgpSubstrate:true, pgpInhibitor:false, halfLife:40, tmax:2, bioavailability:0.70, therapeuticIndex:'narrow', organEffects:['Inotropic effect','AV node block','Arrhythmia risk'], clearance:0.088, dose_mg:0.25, color:'#826853', receptorAffinity:0.92, // Na+/K+ ATPase affectedSystems:['cardiovascular'] }, { id:'omeprazole', name:'Omeprazole', mw:345, logp:2.2, proteinBinding:0.95, cypSubstrates:['CYP2C19'], cypInhibitors:['CYP2C19'], pgpSubstrate:false, pgpInhibitor:false, halfLife:0.7, tmax:0.5, bioavailability:0.40, therapeuticIndex:'wide', organEffects:['Acid suppression','Vitamin B12 deficiency'], clearance:1.2, dose_mg:20, color:'#d9a85f', receptorAffinity:0.88, // proton pump affectedSystems:['gi'] }, { id:'clopidogrel', name:'Clopidogrel', mw:322, logp:3.8, proteinBinding:0.98, cypSubstrates:['CYP2C19'], cypInhibitors:[], pgpSubstrate:false, pgpInhibitor:false, halfLife:6, tmax:1, bioavailability:0.50, therapeuticIndex:'moderate', organEffects:['Antiplatelet','Bleeding risk'], clearance:0.45, dose_mg:75, color:'#b07c44', receptorAffinity:0.82, // P2Y12 irreversible affectedSystems:['coagulation'] }, { id:'diazepam', name:'Diazepam', mw:285, logp:2.8, proteinBinding:0.98, cypSubstrates:['CYP3A4','CYP2C19'], cypInhibitors:[], pgpSubstrate:false, pgpInhibitor:false, halfLife:36, tmax:1.5, bioavailability:1.0, therapeuticIndex:'wide', organEffects:['Sedation','Anxiolysis','Respiratory depression'], clearance:0.19, dose_mg:10, color:'#9c7a55', receptorAffinity:0.90, // GABA-A affectedSystems:['cns'] }, { id:'ibuprofen', name:'Ibuprofen', mw:206, logp:3.5, proteinBinding:0.99, cypSubstrates:['CYP2C9'], cypInhibitors:[], pgpSubstrate:false, pgpInhibitor:false, halfLife:2, tmax:1, bioavailability:0.80, therapeuticIndex:'wide', organEffects:['Anti-inflammatory','GI bleeding','Renal impairment'], clearance:0.90, dose_mg:400, color:'#d6a23a', receptorAffinity:0.70, // COX inhibition affectedSystems:['coagulation','gi','renal'] } ]; // ─── CYP INHIBITION POTENCIES (Ki-like scaling) ─── // How strongly each inhibitor affects each CYP enzyme // Values represent fractional inhibition at clinical dose (0-1) const CYP_INHIBITION_POTENCY = { 'fluconazole': {'CYP2C9':0.65, 'CYP3A4':0.40}, 'amiodarone': {'CYP3A4':0.55, 'CYP2C9':0.50}, 'clarithromycin':{'CYP3A4':0.75}, 'omeprazole': {'CYP2C19':0.60} }; // ─── STATE ─── let selectedDrugs = []; // ─── PHARMACOLOGY ENGINE ─── function computeInteractions(drugs) { const results = []; const pairResults = []; for (let i = 0; i < drugs.length; i++) { for (let j = i+1; j < drugs.length; j++) { const a = drugs[i], b = drugs[j]; const pair = computePairwise(a, b); pairResults.push(pair); } } // Aggregate per-drug effects for (const drug of drugs) { let aucMultiplier = 1.0; let cmaxMultiplier = 1.0; let freeFractionIncrease = 0; const mechanisms = []; const cypDetails = []; for (const pr of pairResults) { if (pr.drugA.id === drug.id) { aucMultiplier *= pr.aucEffectOnA; cmaxMultiplier *= pr.cmaxEffectOnA; freeFractionIncrease += pr.freeFractionIncreaseA; mechanisms.push(...pr.mechanismsA); cypDetails.push(...pr.cypDetailsA); } if (pr.drugB.id === drug.id) { aucMultiplier *= pr.aucEffectOnB; cmaxMultiplier *= pr.cmaxEffectOnB; freeFractionIncrease += pr.freeFractionIncreaseB; mechanisms.push(...pr.mechanismsB); cypDetails.push(...pr.cypDetailsB); } } results.push({ drug, aucMultiplier: Math.min(aucMultiplier, 8), cmaxMultiplier: Math.min(cmaxMultiplier, 5), freeFractionIncrease: Math.min(freeFractionIncrease, 0.5), mechanisms: [...new Set(mechanisms)], cypDetails }); } // Compute overall severity const severity = computeSeverity(results, pairResults); // Compute PD additive effects const pdEffects = computePDEffects(drugs, results); return { perDrug: results, pairs: pairResults, severity, pdEffects }; } function computePairwise(a, b) { // CYP-mediated interactions let aucEffectOnA = 1.0, aucEffectOnB = 1.0; let cmaxEffectOnA = 1.0, cmaxEffectOnB = 1.0; const mechanismsA = [], mechanismsB = []; const cypDetailsA = [], cypDetailsB = []; // Does B inhibit any CYP that A is a substrate of? for (const sub of a.cypSubstrates) { const inhib = getCYPInhibition(b, sub); if (inhib > 0) { // AUC increase = 1/(1 - inhibition_fraction) const factor = 1 / (1 - inhib); aucEffectOnA *= factor; cmaxEffectOnA *= (1 + (factor - 1) * 0.6); // Cmax doesn't rise as much as AUC mechanismsA.push(`CYP inhibition`); cypDetailsA.push({enzyme: sub, inhibitor: b.name, inhibited: a.name, fraction: inhib, aucFactor: factor}); } } // Does A inhibit any CYP that B is a substrate of? for (const sub of b.cypSubstrates) { const inhib = getCYPInhibition(a, sub); if (inhib > 0) { const factor = 1 / (1 - inhib); aucEffectOnB *= factor; cmaxEffectOnB *= (1 + (factor - 1) * 0.6); mechanismsB.push(`CYP inhibition`); cypDetailsB.push({enzyme: sub, inhibitor: a.name, inhibited: b.name, fraction: inhib, aucFactor: factor}); } } // P-gp interactions if (a.pgpInhibitor && b.pgpSubstrate) { const pgpFactor = 1.6; aucEffectOnB *= pgpFactor; cmaxEffectOnB *= 1.3; mechanismsB.push('P-gp inhibition'); cypDetailsB.push({enzyme:'P-gp', inhibitor:a.name, inhibited:b.name, fraction:0.38, aucFactor:pgpFactor}); } if (b.pgpInhibitor && a.pgpSubstrate) { const pgpFactor = 1.6; aucEffectOnA *= pgpFactor; cmaxEffectOnA *= 1.3; mechanismsA.push('P-gp inhibition'); cypDetailsA.push({enzyme:'P-gp', inhibitor:b.name, inhibited:a.name, fraction:0.38, aucFactor:pgpFactor}); } // Protein binding displacement let freeFractionIncreaseA = 0, freeFractionIncreaseB = 0; if (a.proteinBinding > 0.7 && b.proteinBinding > 0.7) { // Both highly protein bound → displacement competition const fa = 1 - a.proteinBinding; // free fraction const fb = 1 - b.proteinBinding; // Displacement modeled by competitive binding to albumin // The drug with higher LogP tends to displace more const logPRatio = b.logp / (a.logp + b.logp); const displacementOnA = logPRatio * fb * 2.5; // scaled const logPRatio2 = a.logp / (a.logp + b.logp); const displacementOnB = logPRatio2 * fa * 2.5; freeFractionIncreaseA = Math.min(displacementOnA, 0.3); freeFractionIncreaseB = Math.min(displacementOnB, 0.3); if (freeFractionIncreaseA > 0.02) mechanismsA.push('Protein displacement'); if (freeFractionIncreaseB > 0.02) mechanismsB.push('Protein displacement'); } return { drugA: a, drugB: b, aucEffectOnA, aucEffectOnB, cmaxEffectOnA, cmaxEffectOnB, freeFractionIncreaseA, freeFractionIncreaseB, mechanismsA, mechanismsB, cypDetailsA, cypDetailsB }; } function getCYPInhibition(drug, enzyme) { const pot = CYP_INHIBITION_POTENCY[drug.id]; if (!pot) return 0; return pot[enzyme] || 0; } function computeSeverity(perDrug, pairs) { let maxSeverity = 'green'; // default let details = []; let guidance = []; for (const pd of perDrug) { const auc = pd.aucMultiplier; const narrow = pd.drug.therapeuticIndex === 'narrow'; const moderate = pd.drug.therapeuticIndex === 'moderate'; if (auc > 3.0 && narrow) { if (maxSeverity !== 'black') maxSeverity = 'red'; details.push(`${pd.drug.name}: AUC ${auc.toFixed(1)}x increase (narrow TI)`); guidance.push(`Illustrative threshold reached for ${pd.drug.name}; this is not a clinical risk classification or dosing recommendation.`); } else if (auc > 2.0 && narrow) { if (['green','yellow'].includes(maxSeverity)) maxSeverity = 'red'; details.push(`${pd.drug.name}: AUC ${auc.toFixed(1)}x increase (narrow TI)`); guidance.push(`Illustrative threshold reached for ${pd.drug.name}; this is not a clinical risk classification or dosing recommendation.`); } else if (auc > 2.0 && moderate) { if (['green','yellow'].includes(maxSeverity)) maxSeverity = 'orange'; details.push(`${pd.drug.name}: AUC ${auc.toFixed(1)}x increase`); guidance.push(`Illustrative threshold reached for ${pd.drug.name}; this is not a clinical risk classification or dosing recommendation.`); } else if (auc > 1.5 && narrow) { if (['green'].includes(maxSeverity)) maxSeverity = 'orange'; details.push(`${pd.drug.name}: AUC ${auc.toFixed(1)}x increase (narrow TI)`); guidance.push(`Illustrative threshold reached for ${pd.drug.name}; this is not a clinical risk classification or dosing recommendation.`); } else if (auc > 1.3) { if (maxSeverity === 'green') maxSeverity = 'yellow'; details.push(`${pd.drug.name}: AUC ${auc.toFixed(1)}x increase`); guidance.push(`Illustrative threshold reached for ${pd.drug.name}; this is not a clinical risk classification or dosing recommendation.`); } else { details.push(`${pd.drug.name}: No threshold crossing in this simplified PK model`); } // Protein displacement severity if (pd.freeFractionIncrease > 0.1 && narrow) { if (['green','yellow'].includes(maxSeverity)) maxSeverity = 'orange'; details.push(`${pd.drug.name}: Free fraction increased ${(pd.freeFractionIncrease*100).toFixed(0)}% by displacement`); guidance.push(`Illustrative threshold reached for ${pd.drug.name}; this is not a clinical risk classification or dosing recommendation.`); } else if (pd.freeFractionIncrease > 0.05) { if (maxSeverity === 'green') maxSeverity = 'yellow'; details.push(`${pd.drug.name}: Free fraction increased ${(pd.freeFractionIncrease*100).toFixed(0)}% by displacement`); } } // Upper illustrative exposure bin, not a clinical contraindication classification for (const pd of perDrug) { if (pd.aucMultiplier > 4.0 && pd.drug.therapeuticIndex === 'narrow') { maxSeverity = 'black'; guidance.unshift(`Large modeled exposure shift for ${pd.drug.name}; this heuristic cannot quantify patient risk.`); } } return { level: maxSeverity, details, guidance: guidance.join(' ') || 'No threshold crossing in this simplified model does not establish safety.', label: { green:'Below illustrative thresholds', yellow:'Small model signal', orange:'Intermediate model signal', red:'Large model signal', black:'Very large model signal' }[maxSeverity] }; } function computePDEffects(drugs, perDrug) { const effects = []; const systemMap = {}; for (const drug of drugs) { for (const sys of drug.affectedSystems) { if (!systemMap[sys]) systemMap[sys] = []; systemMap[sys].push(drug); } } for (const [system, drugsInSys] of Object.entries(systemMap)) { if (drugsInSys.length < 2) continue; // Bliss independence: E_combined = E_A + E_B - E_A * E_B const effects_drugs = drugsInSys.map(d => { const pd = perDrug.find(p => p.drug.id === d.id); const boosted = pd ? pd.aucMultiplier : 1; return Math.min(d.receptorAffinity * Math.min(boosted, 3) * 0.5, 1); }); let combined = effects_drugs[0]; for (let i = 1; i < effects_drugs.length; i++) { combined = combined + effects_drugs[i] - combined * effects_drugs[i]; } const sysName = { coagulation:'Coagulation System', cardiovascular:'Cardiovascular System', cns:'Central Nervous System', gi:'Gastrointestinal System', musculoskeletal:'Musculoskeletal System', hepatic:'Hepatic System', renal:'Renal System', thyroid:'Thyroid', fungal:'Antifungal model component' }[system] || system; let risk = 'low'; if (combined > 0.8) risk = 'high'; else if (combined > 0.5) risk = 'moderate'; effects.push({ system: sysName, drugs: drugsInSys.map(d => d.name), combinedEffect: combined, risk, model: 'Bliss Independence' }); } return effects; } // ─── PK CURVE COMPUTATION ─── // One-compartment oral model: C(t) = (F*D/V) * (ka/(ka-ke)) * (e^(-ke*t) - e^(-ka*t)) function computeConcCurve(drug, aucMultiplier, hours, nPoints) { const ka = Math.log(2) / (drug.tmax / 2.5); // absorption half-life approximation const ke = Math.log(2) / drug.halfLife; const F = drug.bioavailability; const D = drug.dose_mg * 1000; // mcg const V = drug.mw * 0.8; // rough volume in L const points = []; const dt = hours / nPoints; for (let i = 0; i <= nPoints; i++) { const t = i * dt; let C; if (t === 0) { C = 0; } else { const denom = ka - ke; C = (F * D / V) * (ka / Math.abs(denom)) * (Math.exp(-ke * t) - Math.exp(-ka * t)); } // Apply interaction multiplier (time-varying: peaks during absorption) // The interaction primarily affects clearance, so the multiplier grows over time const timeFactor = 1 + (aucMultiplier - 1) * (1 - Math.exp(-0.1 * t)); points.push({ t, C_normal: C, C_interacted: C * timeFactor }); } return points; } function computeOccupancy(drug, aucMultiplier, hours, nPoints) { const ka = Math.log(2) / (drug.tmax / 2.5); const ke = Math.log(2) / drug.halfLife; const F = drug.bioavailability; const D = drug.dose_mg * 1000; const V = drug.mw * 0.8; const Kd = drug.mw * 0.15; // dissociation constant scaled const points = []; const dt = hours / nPoints; for (let i = 0; i <= nPoints; i++) { const t = i * dt; let C; if (t === 0) { C = 0; } else { const denom = ka - ke; C = (F * D / V) * (ka / Math.abs(denom)) * (Math.exp(-ke * t) - Math.exp(-ka * t)); } const timeFactor = 1 + (aucMultiplier - 1) * (1 - Math.exp(-0.1 * t)); const Ci = C * timeFactor; // Emax model for occupancy const occ_normal = (C / (C + Kd)) * drug.receptorAffinity * 100; const occ_interacted = (Ci / (Ci + Kd)) * drug.receptorAffinity * 100; points.push({ t, occ_normal: Math.min(occ_normal, 100), occ_interacted: Math.min(occ_interacted, 100) }); } return points; } // ─── RENDERING ─── function renderDrugList() { const el = document.getElementById('drugList'); const focusedId = document.activeElement && document.activeElement.getAttribute('data-drug-id'); el.innerHTML = ''; DRUGS.forEach((drug, idx) => { const sel = selectedDrugs.find(d => d.id === drug.id); const full = selectedDrugs.length >= 3 && !sel; const card = document.createElement('button'); card.type = 'button'; card.setAttribute('data-drug-id', drug.id); card.disabled = !!full; card.setAttribute('aria-disabled', String(!!full)); card.setAttribute('aria-pressed', String(selectedDrugs.some(d => d.id === drug.id))); card.className = 'drug-card' + (sel ? ' selected' : '') + (full ? ' disabled' : ''); const tags = [ ...drug.cypSubstrates.map(s => `${s} sub`), ...drug.cypInhibitors.map(s => `${s} inh`), ...(drug.pgpSubstrate ? [`P-gp sub`] : []), ...(drug.pgpInhibitor ? [`P-gp inh`] : []) ].join(''); card.innerHTML = `
${cyp.inhibitor} inhibits ${cyp.enzyme}, which metabolizes ${cyp.inhibited}. This reduces ${cyp.inhibited} clearance by ${(cyp.fraction*100).toFixed(0)}%, increasing AUC by ${(cyp.aucFactor).toFixed(1)}x.
${inhibitor.name} inhibits P-gp transport, reducing efflux of ${substrate.name}. This increases absorption and decreases renal/biliary clearance, raising systemic exposure ~1.6x.
${displacer.name} (LogP ${displacer.logp}, ${(displacer.proteinBinding*100).toFixed(0)}% bound) displaces ${displaced.name} (LogP ${displaced.logp}, ${(displaced.proteinBinding*100).toFixed(0)}% bound) from albumin binding sites. Free fraction of ${displaced.name} increases by ~${(increase*100).toFixed(0)}%, amplifying its pharmacological effect.
${pd.drugs.join(' + ')} both affect the ${pd.system}. Combined effect computed via ${pd.model}: E = ${(pd.combinedEffect*100).toFixed(0)}% (illustrative signal: ${pd.risk}).
No mechanism crossed the display threshold in this simplified model. Real interactions may be absent from the model.