Chapter 2: Stereochemistry - Optical Isomers#

1. Introduction#

Stereochemistry—the three-dimensional arrangement of atoms in molecules—is fundamental to drug discovery because biological systems are inherently chiral (specific 3D shaped) environments. Proteins, nucleic acids, and carbohydrates exist predominantly as single stereoisomers, making them exquisitely sensitive to the 3D shape of drug molecules. A drug’s stereochemistry can determine whether it binds to its target protein, gets metabolized safely, or causes harmful side effects.

Molecules with the same molecular formula but different arrangements of atoms are are called isomers.

There are a few types of isomers, but we will be focusing on stereoisomers.

Types of Stereisomers Figure adapted from source

Stereoisomers can be further subdivided into:

  • Enantiomers (a type of optical isomers)

  • Diastereomers (a type of geometric isomers)

This section will guide you through the fundamental principles of stereoisomerism and chirality. You will learn to identify chiral centers and identify optical isomers.


2. Key Concepts and Definitions#

  • Chirality: A geometric property where a molecule cannot be superimposed on its mirror image, like left and right hands. * Chiral molecules lack an internal plane of symmetry.

  • Enantiomers: A pair of molecules that are non-superimposable mirror images of each other. They have identical physical properties (melting point, boiling point) except for how they interact with polarized light and other chiral environments.

  • Chiral Center (Stereocenter): Most commonly a carbon atom bonded to four different substituents, creating the potential for mirror-image forms. Also called an asymmetric carbon.

  • Racemic Mixture: A 50:50 mixture of both enantiomers (±), which shows no net optical rotation because the two enantiomers’ effects cancel out.


3. Main Content#

3.1 Understanding Chirality: The Handedness of Molecules#

Chirality arises when a molecule lacks superimposability with its mirror image. The most common source is a tetrahedral carbon atom bonded to four different groups. To determine if a molecule is chiral, look for:

  • Presence of a chiral center - typically C with four different substituents

  • Non-superimposable mirror images - like gloves, the left doesn’t fit the right

Hide code cell source

from rdkit import Chem
from rdkit.Chem import AllChem
import numpy as np
from IPython.display import HTML
import random

def create_chiral_enantiomers():
    """Create CHFClBrI enantiomers - a chiral carbon with 4 different halogens"""
    
    # SMILES for R and S configurations
    # The molecule is CHFClBrI where C is the chiral center
    smiles_R = "[C@](F)(Cl)(Br)I"  # R-enantiomer
    smiles_S = "[C@@](F)(Cl)(Br)I"  # S-enantiomer (mirror image)
    
    # Create molecules
    mol_R = Chem.MolFromSmiles(smiles_R)
    mol_S = Chem.MolFromSmiles(smiles_S)
    
    if mol_R is None or mol_S is None:
        print("Error creating molecules from SMILES")
        return None, None
    
    # Add explicit hydrogens (already has H in SMILES)
    mol_R = Chem.AddHs(mol_R)
    mol_S = Chem.AddHs(mol_S)
    
    # Generate 3D coordinates with same random seed for similar initial orientations
    AllChem.EmbedMolecule(mol_R, randomSeed=42)
    AllChem.MMFFOptimizeMolecule(mol_R)
    
    AllChem.EmbedMolecule(mol_S, randomSeed=42)
    AllChem.MMFFOptimizeMolecule(mol_S)
    
    return mol_R, mol_S

def mol_to_xyz(mol):
    """Convert RDKit molecule to XYZ format string"""
    conf = mol.GetConformer()
    xyz_lines = [str(mol.GetNumAtoms()), ""]
    
    for atom_idx in range(mol.GetNumAtoms()):
        atom = mol.GetAtomWithIdx(atom_idx)
        pos = conf.GetAtomPosition(atom_idx)
        symbol = atom.GetSymbol()
        xyz_lines.append(f"{symbol} {pos.x:.4f} {pos.y:.4f} {pos.z:.4f}")
    
    return "\n".join(xyz_lines)

def create_superposition_challenge():
    """Create interactive viewer for learning about non-superimposable mirror images"""
    
    # Generate molecules
    mol_R, mol_S = create_chiral_enantiomers()
    
    if mol_R is None or mol_S is None:
        return HTML("<p>Error generating molecules</p>")
    
    # Convert to XYZ
    xyz_R = mol_to_xyz(mol_R)
    xyz_S = mol_to_xyz(mol_S)
    
    # Generate unique IDs
    rand_id = random.randint(100000, 999999)
    viewer_id_R = f"vR{rand_id}"
    viewer_id_S = f"vS{rand_id}"
    btn_id = f"btn{rand_id}"
    
    # Color scheme from guide
    color_R = '#9C27B0'  # Purple
    bg_R = '#f3e5f5'
    color_S = '#2196F3'  # Blue
    bg_S = '#f0f8ff'
    
    html_content = f"""
    <div style="font-family: Arial, sans-serif; max-width: 1200px; margin: 20px auto;">
        
        <div style="background: #fff3e0; border-left: 4px solid #ff9800; padding: 15px; margin-bottom: 20px; border-radius: 4px;">
            <h4 style="margin: 0 0 10px 0; color: #e65100;">📋 Instructions:</h4>
            <ol style="margin: 5px 0; padding-left: 20px; color: #666;">
                <li><strong>Rotate the left molecule</strong> (click and drag) to any orientation you like</li>
                <li><strong>Try to rotate the right molecule</strong> to make it look identical to the left one</li>
                <li><strong>Challenge:</strong> Can you superimpose them perfectly?</li>
            </ol>
        </div>
        
        <!-- Side by side viewers -->
        <div style="display: flex; gap: 20px; margin-bottom: 20px;">
            
            <!-- R-enantiomer Section (LEFT - will be locked) -->
            <div style="flex: 1; border: 3px solid {color_R}; border-radius: 10px; padding: 15px; background: white;">
                <div style="text-align: center; margin-bottom: 15px;">
                    <h3 style="color: {color_R}; margin: 0;">R-Enantiomer</h3>
                    <p style="color: #666; font-size: 13px; margin: 5px 0;">CHFClBrI</p>
                    <p id="status_R" style="color: #4CAF50; font-size: 12px; margin: 5px 0; font-weight: bold;">
                        🔄 Click and drag to rotate
                    </p>
                </div>
                
                <div id="{viewer_id_R}" style="position: relative; width: 100%; height: 450px; 
                     background-color: {bg_R}; border-radius: 8px;"></div>
                     
                <div style="margin-top: 10px; padding: 10px; background: #f5f5f5; border-radius: 6px;">
                    <p style="margin: 0; font-size: 12px; color: #666; text-align: center;">
                        <strong>Chiral Center:</strong> Carbon with F, Cl, Br, I (4 different groups)
                    </p>
                </div>
            </div>
            
            <!-- S-enantiomer Section (RIGHT - always rotatable) -->
            <div style="flex: 1; border: 3px solid {color_S}; border-radius: 10px; padding: 15px; background: white;">
                <div style="text-align: center; margin-bottom: 15px;">
                    <h3 style="color: {color_S}; margin: 0;">S-Enantiomer</h3>
                    <p style="color: #666; font-size: 13px; margin: 5px 0;">CHFClBrI (Mirror Image)</p>
                    <p style="color: {color_S}; font-size: 12px; margin: 5px 0; font-weight: bold;">
                        🔄 Always rotatable - try to match the left!
                    </p>
                </div>
                
                <div id="{viewer_id_S}" style="position: relative; width: 100%; height: 450px; 
                     background-color: {bg_S}; border-radius: 8px;"></div>
                     
                <div style="margin-top: 10px; padding: 10px; background: #f5f5f5; border-radius: 6px;">
                    <p style="margin: 0; font-size: 12px; color: #666; text-align: center;">
                        <strong>Mirror Image:</strong> Same atoms, opposite spatial arrangement
                    </p>
                </div>
            </div>
            
        </div>
        
        <div style="background: #e8f5e9; border-left: 4px solid #4CAF50; padding: 15px; border-radius: 4px;">
            <h4 style="margin: 0 0 10px 0; color: #2e7d32;">💡 What You Should Discover</h4>
            <p style="margin: 5px 0; color: #666;">
                <strong>You cannot superimpose them!</strong> These molecules are <strong>non-superimposable mirror images</strong> 
                (enantiomers). No matter how you rotate the right molecule, you cannot make all four substituents align 
                with the left molecule simultaneously.
            </p>
            <p style="margin: 10px 0 5px 0; color: #666;">
                <strong>Key insight:</strong> Chirality means "handedness" - like your left and right hands, these molecules 
                are mirror images but cannot be superimposed. The <strong style="color: {color_R};">R-enantiomer</strong> and 
                <strong style="color: {color_S};">S-enantiomer</strong> are fundamentally different molecules with potentially 
                different biological activities.
            </p>
            <p style="margin: 10px 0 5px 0; color: #666;">
                <strong>In drug discovery:</strong> One enantiomer might be therapeutic while its mirror image could be 
                inactive or even harmful! This is why understanding chirality is crucial.
            </p>
        </div>
        
    </div>
    
    <script>
    (function() {{
        var xyz_R = `{xyz_R}`;
        var xyz_S = `{xyz_S}`;
        var viewer_R = null;
        var viewer_S = null;
        var isLocked = false;
        
        function initViewers() {{
            // Create R-enantiomer viewer (LEFT - will be lockable)
            var container_R = document.getElementById('{viewer_id_R}');
            
            if (container_R && typeof $3Dmol !== 'undefined') {{
                viewer_R = $3Dmol.createViewer(container_R, {{
                    backgroundColor: '{bg_R}',
                    disableFog: true
                }});
                
                viewer_R.addModel(xyz_R, 'xyz');
                viewer_R.setStyle({{}}, {{
                    stick: {{radius: 0.2, colorscheme: 'Jmol'}},
                    sphere: {{scale: 0.35, colorscheme: 'Jmol'}}
                }});
                
                viewer_R.zoomTo();
                viewer_R.render();
            }}
            
            // Create S-enantiomer viewer (RIGHT - always rotatable)
            var container_S = document.getElementById('{viewer_id_S}');
            
            if (container_S && typeof $3Dmol !== 'undefined') {{
                viewer_S = $3Dmol.createViewer(container_S, {{
                    backgroundColor: '{bg_S}',
                    disableFog: true
                }});
                
                viewer_S.addModel(xyz_S, 'xyz');
                viewer_S.setStyle({{}}, {{
                    stick: {{radius: 0.2, colorscheme: 'Jmol'}},
                    sphere: {{scale: 0.35, colorscheme: 'Jmol'}}
                }});
                viewer_S.zoomTo();
                viewer_S.render();
            }}
        }}
    
        
        // Initialize with delays
        setTimeout(initViewers, 500);
        setTimeout(initViewers, 1500);
    }})();
    </script>
    """
    
    return HTML(html_content)

# Run the interactive challenge
create_superposition_challenge()

📋 Instructions:

  1. Rotate the left molecule (click and drag) to any orientation you like
  2. Try to rotate the right molecule to make it look identical to the left one
  3. Challenge: Can you superimpose them perfectly?

R-Enantiomer

CHFClBrI

🔄 Click and drag to rotate

Chiral Center: Carbon with F, Cl, Br, I (4 different groups)

S-Enantiomer

CHFClBrI (Mirror Image)

🔄 Always rotatable - try to match the left!

Mirror Image: Same atoms, opposite spatial arrangement

💡 What You Should Discover

You cannot superimpose them! These molecules are non-superimposable mirror images (enantiomers). No matter how you rotate the right molecule, you cannot make all four substituents align with the left molecule simultaneously.

Key insight: Chirality means "handedness" - like your left and right hands, these molecules are mirror images but cannot be superimposed. The R-enantiomer and S-enantiomer are fundamentally different molecules with potentially different biological activities.

In drug discovery: One enantiomer might be therapeutic while its mirror image could be inactive or even harmful! This is why understanding chirality is crucial.

Hide code cell source

from rdkit import Chem
from rdkit.Chem import AllChem
import numpy as np
import json
from IPython.display import HTML

def identify_chiral_centers(mol):
    """Identify all chiral carbon atoms in a molecule"""
    chiral_centers = []
    for atom in mol.GetAtoms():
        if atom.GetSymbol() == 'C' and atom.GetChiralTag() != Chem.ChiralType.CHI_UNSPECIFIED:
            chiral_centers.append(atom.GetIdx())
    return chiral_centers

def create_chiral_center_game():
    """Create interactive game for identifying chiral centers"""
    
    # Define molecules
    molecules = {
        'level1': {
            'name': 'Lactic Acid',
            'smiles': 'C[C@H](O)C(=O)O',
            'description': '1 chiral center - Common in metabolism',
            'difficulty': 'Beginner'
        },
        'level2': {
            'name': 'Pseudoephedrine',
            'smiles': 'C[C@@H](N)[C@@H](O)c1ccccc1',
            'description': '2 chiral centers - Decongestant drug',
            'difficulty': 'Intermediate'
        },
        'level3': {
            'name': 'Atorvastatin Fragment',
            'smiles': 'C[C@H](CC[C@H](O)C[C@H](O)CC(=O)O)c1ccccc1',
            'description': '4 chiral centers - Cholesterol-lowering drug',
            'difficulty': 'Advanced'
        }
    }
    
    # Generate 3D structures
    mol_data = {}
    for level, data in molecules.items():
        mol = Chem.MolFromSmiles(data['smiles'])
        mol = Chem.AddHs(mol)
        
        # Robust embedding: Try standard, then random coords if standard fails
        res = AllChem.EmbedMolecule(mol, randomSeed=42)
        if res == -1:
            AllChem.EmbedMolecule(mol, useRandomCoords=True)
            
        AllChem.MMFFOptimizeMolecule(mol)
        
        chiral_centers = identify_chiral_centers(mol)
        xyz_block = Chem.MolToXYZBlock(mol)
        
        mol_data[level] = {
            'xyz': xyz_block,
            'chiral_centers': chiral_centers,
            'num_chiral': len(chiral_centers),
            'name': data['name'],
            'description': data['description'],
            'difficulty': data['difficulty']
        }
    
    mol_data_json = json.dumps(mol_data)
    viewer_id = f"chiral_viewer_{np.random.randint(100000, 999999)}"
    
    html = f"""
    <!DOCTYPE html>
    <html>
    <head>
        <script src="https://3Dmol.csb.pitt.edu/build/3Dmol-min.js"></script>
        <style>
            .game-container {{
                font-family: 'Segoe UI', Arial, sans-serif;
                max-width: 800px; margin: 0 auto; padding: 20px;
                background: #fff; border-radius: 12px;
                box-shadow: 0 4px 12px rgba(0,0,0,0.1); border: 1px solid #ddd;
            }}
            .header {{ text-align: center; margin-bottom: 25px; }}
            .header h1 {{ color: #2c3e50; margin: 0; }}
            .level-selector {{ display: flex; gap: 10px; margin-bottom: 20px; }}
            .level-btn {{
                flex: 1; padding: 12px; border: 2px solid #eee;
                border-radius: 8px; cursor: pointer; transition: all 0.2s;
                text-align: center; background: #f8f9fa;
            }}
            .level-btn:hover {{ border-color: #3498db; background: #ebf5fb; }}
            .level-btn.active {{ border-color: #3498db; background: #e3f2fd; color: #2980b9; }}
            
            .viewer-wrapper {{
                height: 400px; width: 100%; position: relative;
                border: 1px solid #eee; border-radius: 8px; overflow: hidden;
            }}
            
            .status-bar {{
                display: flex; justify-content: space-between;
                padding: 15px; background: #f8f9fa; border-radius: 8px; margin-bottom: 15px;
            }}
            .stat-box {{ text-align: center; }}
            .stat-val {{ font-size: 20px; font-weight: bold; color: #2c3e50; }}
            .stat-label {{ font-size: 12px; color: #7f8c8d; text-transform: uppercase; }}
            
            .completion-msg {{
                display: none; background: #d4edda; color: #155724;
                padding: 15px; border-radius: 8px; text-align: center; margin-top: 15px;
                border: 1px solid #c3e6cb;
            }}
            
            .btn-reset {{
                display: block; width: 100%; padding: 12px; margin-top: 15px;
                background: #3498db; color: white; border: none; border-radius: 6px;
                cursor: pointer; font-weight: 600;
            }}
            .btn-reset:hover {{ background: #2980b9; }}
        </style>
    </head>
    <body>
        <div class="game-container">

            <div style="background: #fff3e0; border-left: 4px solid #ff9800; padding: 15px; margin-bottom: 20px; border-radius: 4px;">
                <h4 style="margin: 0 0 10px 0; color: #e65100;">📋 Instructions:</h4>
                <ol style="margin: 5px 0; padding-left: 20px; color: #666;">
                    <li><strong>Select the chiral centres in the molecule</li>
                    <p>Remember that chiral centres are atoms that are attached to four different groups of atoms</p>
                </ol>
            </div>
            
            <div class="level-selector">
                <div class="level-btn active" onclick="window.game_{viewer_id}.setLevel('level1', this)">
                    <strong>Level 1</strong><br><span style="font-size:12px">Lactic Acid</span>
                </div>
                <div class="level-btn" onclick="window.game_{viewer_id}.setLevel('level2', this)">
                    <strong>Level 2</strong><br><span style="font-size:12px">Pseudoephedrine</span>
                </div>
                <div class="level-btn" onclick="window.game_{viewer_id}.setLevel('level3', this)">
                    <strong>Level 3</strong><br><span style="font-size:12px">Atorvastatin</span>
                </div>
            </div>

            <div class="status-bar">
                <div class="stat-box">
                    <div class="stat-label">Total</div>
                    <div class="stat-val" id="total_{viewer_id}">0</div>
                </div>
                <div class="stat-box">
                    <div class="stat-label">Found</div>
                    <div class="stat-val" style="color:#27ae60" id="found_{viewer_id}">0</div>
                </div>
                <div class="stat-box">
                    <div class="stat-label">Remaining</div>
                    <div class="stat-val" style="color:#e74c3c" id="rem_{viewer_id}">0</div>
                </div>
            </div>

            <div id="viewer_{viewer_id}" class="viewer-wrapper"></div>
            
            <div id="msg_{viewer_id}" class="completion-msg">
                <strong>🎉 Success!</strong> You identified all chiral centers.
            </div>
            
            <button class="btn-reset" onclick="window.game_{viewer_id}.reset()">🔄 Reset Level</button>
            
            <div style="margin-top:20px; font-size: 13px; color: #666; background: #fff3cd; padding: 10px; border-radius: 6px;">
                <strong>💡 Tip:</strong> Click atoms to identify them. Green = Correct, Red = Incorrect.
                Drag to rotate, scroll to zoom.
            </div>
        </div>

        <script>
        // Namespace the game to avoid collisions if multiple viewers exist
        window.game_{viewer_id} = (function() {{
            const molecules = {mol_data_json};
            let viewer = null;
            let currentLevel = 'level1';
            let found = new Set();
            let clicked = new Set(); // Debounce clicks
            
            function init() {{
                viewer = $3Dmol.createViewer("viewer_{viewer_id}", {{
                    backgroundColor: 'white'
                }});
                loadLevel('level1');
            }}
            
            function loadLevel(level) {{
                currentLevel = level;
                found.clear();
                clicked.clear();
                document.getElementById('msg_{viewer_id}').style.display = 'none';
                
                let data = molecules[level];
                viewer.clear();
                viewer.addModel(data.xyz, "xyz");
                
                // Style: Sticks + small spheres for atoms
                viewer.setStyle({{}}, {{stick: {{radius: 0.15}}, sphere: {{scale: 0.3}}}});
                
                // Click handler
                viewer.setClickable({{}}, true, function(atom) {{
                    handleAtomClick(atom);
                }});
                
                viewer.zoomTo();
                viewer.render();
                updateStats();
            }}
            
            function handleAtomClick(atom) {{
                if(clicked.has(atom.index)) return; // Prevent double clicks
                
                let data = molecules[currentLevel];
                let isChiral = data.chiral_centers.includes(atom.index);
                
                if(isChiral) {{
                    if(!found.has(atom.index)) {{
                        found.add(atom.index);
                        // Flash Green
                        flashAtom(atom, '#2ecc71');
                        updateStats();
                        checkWin();
                    }}
                }} else {{
                    // Flash Red
                    flashAtom(atom, '#e74c3c');
                }}
            }}
            
            function flashAtom(atom, color) {{
                // Highlight
                viewer.setStyle({{index: atom.index}}, {{
                    stick: {{radius: 0.15}},
                    sphere: {{color: color, scale: 0.6}}
                }});
                viewer.render();
                
                // Reset style after delay
                clicked.add(atom.index);
                setTimeout(() => {{
                    // If it was correct (in found set), keep it green, else revert
                    let finalColor = found.has(atom.index) ? '#2ecc71' : null;
                    let finalScale = found.has(atom.index) ? 0.4 : 0.3;
                    
                    let style = {{ stick: {{radius: 0.15}} }};
                    if(finalColor) {{
                        style.sphere = {{color: finalColor, scale: finalScale}};
                    }} else {{
                        style.sphere = {{scale: 0.3}}; // Revert to default colorscheme
                    }}
                    
                    viewer.setStyle({{index: atom.index}}, style);
                    viewer.render();
                    clicked.delete(atom.index);
                }}, 600);
            }}
            
            function updateStats() {{
                let total = molecules[currentLevel].num_chiral;
                let f = found.size;
                document.getElementById('total_{viewer_id}').innerText = total;
                document.getElementById('found_{viewer_id}').innerText = f;
                document.getElementById('rem_{viewer_id}').innerText = total - f;
            }}
            
            function checkWin() {{
                if(found.size === molecules[currentLevel].num_chiral) {{
                    document.getElementById('msg_{viewer_id}').style.display = 'block';
                }}
            }}
            
            // Initialize after short delay to ensure DOM exists
            setTimeout(init, 100);
            
            // Public API
            return {{
                setLevel: function(level, btnElement) {{
                    // Update UI buttons
                    let btns = document.querySelectorAll('.level-btn');
                    btns.forEach(b => b.classList.remove('active'));
                    if(btnElement) btnElement.classList.add('active');
                    
                    loadLevel(level);
                }},
                reset: function() {{
                    loadLevel(currentLevel);
                }}
            }};
        }})();
        </script>
    </body>
    </html>
    """
    
    return HTML(html)

create_chiral_center_game()

📋 Instructions:

  1. Select the chiral centres in the molecule
  2. Remember that chiral centres are atoms that are attached to four different groups of atoms

Level 1
Lactic Acid
Level 2
Pseudoephedrine
Level 3
Atorvastatin
Total
0
Found
0
Remaining
0
🎉 Success! You identified all chiral centers.
💡 Tip: Click atoms to identify them. Green = Correct, Red = Incorrect. Drag to rotate, scroll to zoom.

Isomers with chiral centres are called optical isomers or also known as enantiomers. You will learn more later.

3.2 Enantiomers: Identical Yet Different#

Enantiomers are non-superimposable mirror-image pairs with similar properties such as melting and boiling point.

However, they are different in optical rotation (how they rotate light, either + or - in magnitude) and more importantly, their biological activity. For example, chiral molecules can taste and smell differently (because olfactory receptors are chiral).

Hide code cell source

from rdkit import Chem
from rdkit.Chem import AllChem
import numpy as np
from IPython.display import HTML
import random

def create_carvone_enantiomers():
    """Create (−)-carvone and (+)-carvone with proper 3D coordinates"""
    
    # SMILES for (S)-(+)-carvone (caraway smell)
    smiles_plus = "CC(=C)[C@@H]1CC=C(C)C(=O)C1"
    # SMILES for (R)-(−)-carvone (spearmint smell)
    smiles_minus = "CC(=C)[C@H]1CC=C(C)C(=O)C1"
    
    # Create molecules
    mol_plus = Chem.MolFromSmiles(smiles_plus)
    mol_minus = Chem.MolFromSmiles(smiles_minus)
    
    if mol_plus is None or mol_minus is None:
        print("Error creating molecules from SMILES")
        return None, None
    
    # Add hydrogens and generate 3D coordinates
    mol_plus = Chem.AddHs(mol_plus)
    mol_minus = Chem.AddHs(mol_minus)
    
    AllChem.EmbedMolecule(mol_plus, randomSeed=42)
    AllChem.MMFFOptimizeMolecule(mol_plus)
    
    AllChem.EmbedMolecule(mol_minus, randomSeed=43)
    AllChem.MMFFOptimizeMolecule(mol_minus)
    
    return mol_plus, mol_minus

def mol_to_xyz(mol):
    """Convert RDKit molecule to XYZ format string"""
    conf = mol.GetConformer()
    xyz_lines = [str(mol.GetNumAtoms()), ""]
    
    for atom_idx in range(mol.GetNumAtoms()):
        atom = mol.GetAtomWithIdx(atom_idx)
        pos = conf.GetAtomPosition(atom_idx)
        symbol = atom.GetSymbol()
        xyz_lines.append(f"{symbol} {pos.x:.4f} {pos.y:.4f} {pos.z:.4f}")
    
    return "\n".join(xyz_lines)

def create_superposition_challenge():
    """Create interactive viewer for trying to superimpose enantiomers"""
    
    # Generate molecules
    mol_plus, mol_minus = create_carvone_enantiomers()
    
    if mol_plus is None or mol_minus is None:
        return HTML("<p>Error generating molecules</p>")
    
    # Convert to XYZ
    xyz_plus = mol_to_xyz(mol_plus)
    xyz_minus = mol_to_xyz(mol_minus)
    
    # Generate unique IDs
    rand_id = random.randint(100000, 999999)
    viewer_id_plus = f"vplus{rand_id}"
    viewer_id_minus = f"vminus{rand_id}"
    
    # Color scheme from guide
    color_plus = '#9C27B0'  # Purple
    bg_plus = '#f3e5f5'
    color_minus = '#2196F3'  # Blue
    bg_minus = '#f0f8ff'
    
    html_content = f"""
    <div style="font-family: Arial, sans-serif; max-width: 1200px; margin: 20px auto;">
        
        <div style="background: #fff3e0; border-left: 4px solid #ff9800; padding: 15px; margin-bottom: 20px; border-radius: 4px;">
            <h4 style="margin: 0 0 10px 0; color: #e65100;">🔄 Interactive Challenge: Try to Superimpose These Molecules</h4>
            <p style="margin: 5px 0; color: #666;">
                The <strong style="color: {color_plus};">(+)-carvone</strong> molecule on the left is <strong>fixed</strong>. 
                Try rotating the <strong style="color: {color_minus};">(−)-carvone</strong> molecule on the right (click and drag) 
                to make it look identical to the left one. Can you succeed?
            </p>
        </div>
        
        <!-- Side by side viewers -->
        <div style="display: flex; gap: 20px; margin-bottom: 20px;">
            
            <!-- (+)-Carvone Section (FIXED) -->
            <div style="flex: 1; border: 3px solid {color_plus}; border-radius: 10px; padding: 15px; background: white;">
                <div style="text-align: center; margin-bottom: 15px;">
                    <h3 style="color: {color_plus}; margin: 0;">(+)-Carvone</h3>
                    <p style="color: #666; font-size: 13px; margin: 5px 0;">Smells like <strong>caraway</strong></p>
                    <p style="color: #999; font-size: 12px; margin: 5px 0;">🔒 FIXED - Cannot rotate</p>
                </div>
                
                <div id="{viewer_id_plus}" style="position: relative; width: 100%; height: 450px; 
                     background-color: {bg_plus}; border-radius: 8px;"></div>
            </div>
            
            <!-- (−)-Carvone Section (ROTATABLE) -->
            <div style="flex: 1; border: 3px solid {color_minus}; border-radius: 10px; padding: 15px; background: white;">
                <div style="text-align: center; margin-bottom: 15px;">
                    <h3 style="color: {color_minus}; margin: 0;">(−)-Carvone</h3>
                    <p style="color: #666; font-size: 13px; margin: 5px 0;">Smells like <strong>spearmint</strong></p>
                    <p style="color: #999; font-size: 12px; margin: 5px 0;">🔄 Click and drag to rotate</p>
                </div>
                
                <div id="{viewer_id_minus}" style="position: relative; width: 100%; height: 450px; 
                     background-color: {bg_minus}; border-radius: 8px;"></div>
            </div>
            
        </div>
        
        <div style="background: #e8f5e9; border-left: 4px solid #4CAF50; padding: 15px; border-radius: 4px;">
            <h4 style="margin: 0 0 10px 0; color: #2e7d32;">💡 What You Should Observe</h4>
            <p style="margin: 5px 0; color: #666;">
                <strong>You cannot superimpose them!</strong> These molecules are <strong>non-superimposable mirror images</strong> 
                (enantiomers). While you can rotate the right molecule to make some parts align with the left, you'll always 
                find other parts pointing in opposite directions.
            </p>
            <p style="margin: 10px 0 5px 0; color: #666;">
                <strong>Key insight:</strong> This is fundamentally different from conformational isomers, which CAN be 
                superimposed through rotation. Enantiomers require breaking and reforming bonds to interconvert—they are 
                truly different molecules, not just different orientations.
            </p>
        </div>
        
    </div>
    
    <script>
    console.log("Script starting...");
    
    (function() {{
        var xyz_plus = `{xyz_plus}`;
        var xyz_minus = `{xyz_minus}`;
        
        console.log("XYZ data loaded");
        console.log("Plus length:", xyz_plus.length);
        console.log("Minus length:", xyz_minus.length);
        
        function initViewers() {{
            console.log("Initializing viewers...");
            console.log("$3Dmol available:", typeof $3Dmol !== 'undefined');
            
            // Create (+)-carvone viewer (FIXED)
            var container_plus = document.getElementById('{viewer_id_plus}');
            console.log("Plus container found:", container_plus !== null);
            
            if (container_plus && typeof $3Dmol !== 'undefined') {{
                try {{
                    var viewer_plus = $3Dmol.createViewer(container_plus, {{
                        backgroundColor: '{bg_plus}',
                        disableFog: true
                    }});
                    
                    viewer_plus.addModel(xyz_plus, 'xyz');
                    viewer_plus.setStyle({{}}, {{
                        stick: {{radius: 0.15, color: 'spectrum'}},
                        sphere: {{scale: 0.3, color: 'spectrum'}}
                    }});
                    viewer_plus.zoomTo();
                    viewer_plus.render();
                    viewer_plus.disableMouseHandlers();
                    
                    console.log("Plus viewer created successfully");
                }} catch(e) {{
                    console.error("Error creating plus viewer:", e);
                }}
            }}
            
            // Create (−)-carvone viewer (ROTATABLE)
            var container_minus = document.getElementById('{viewer_id_minus}');
            console.log("Minus container found:", container_minus !== null);
            
            if (container_minus && typeof $3Dmol !== 'undefined') {{
                try {{
                    var viewer_minus = $3Dmol.createViewer(container_minus, {{
                        backgroundColor: '{bg_minus}',
                        disableFog: true
                    }});
                    
                    viewer_minus.addModel(xyz_minus, 'xyz');
                    viewer_minus.setStyle({{}}, {{
                        stick: {{radius: 0.15, color: 'spectrum'}},
                        sphere: {{scale: 0.3, color: 'spectrum'}}
                    }});
                    viewer_minus.zoomTo();
                    viewer_minus.render();
                    
                    console.log("Minus viewer created successfully");
                }} catch(e) {{
                    console.error("Error creating minus viewer:", e);
                }}
            }}
        }}
        
        // Initialize with delays
        setTimeout(initViewers, 500);
        setTimeout(initViewers, 1500);
    }})();
    </script>
    """
    
    return HTML(html_content)

# Run the interactive challenge
create_superposition_challenge()

🔄 Interactive Challenge: Try to Superimpose These Molecules

The (+)-carvone molecule on the left is fixed. Try rotating the (−)-carvone molecule on the right (click and drag) to make it look identical to the left one. Can you succeed?

(+)-Carvone

Smells like caraway

🔒 FIXED - Cannot rotate

(−)-Carvone

Smells like spearmint

🔄 Click and drag to rotate

💡 What You Should Observe

You cannot superimpose them! These molecules are non-superimposable mirror images (enantiomers). While you can rotate the right molecule to make some parts align with the left, you'll always find other parts pointing in opposite directions.

Key insight: This is fundamentally different from conformational isomers, which CAN be superimposed through rotation. Enantiomers require breaking and reforming bonds to interconvert—they are truly different molecules, not just different orientations.


4. Practical Applications#

  • The Thalidomide Tragedy and Modern Drug Safety: The thalidomide disaster is the most cited example of stereochemistry’s importance. The (R)-enantiomer was an effective sedative, while the (S)-enantiomer was a potent teratogen. Because the two forms could interconvert in the body, administering the “safe” enantiomer was not enough. This led to strict regulations by agencies like the FDA, now requiring that drug candidates with chiral centers be tested for the activity and toxicity of each individual stereoisomer.

  • Chiral Drugs: L-DOPA for Parkinson’s Disease: Parkinson’s disease is characterized by a deficiency of the neurotransmitter dopamine. Dopamine itself cannot cross the blood-brain barrier, but its precursor, L-DOPA, can. L-DOPA is the chiral form of DOPA. Its enantiomer, D-DOPA, is biologically inactive and can cause side effects. This is a classic case where a single, pure enantiomer is marketed as a drug, demonstrating the principle of eurasiomeric pharmacology.


5. Summary and Key Takeaways#

In this section, we’ve explored the fundamental concepts of stereochemistry and identify chiral centres

Key Takeaways:

  • Chirality is fundamental to drug-receptor interactions - biological targets are chiral and can distinguish between enantiomers, often leading to dramatically different pharmacological effects.

  • Enantiomers are mirror images with identical physical properties but different biological activities. One may be therapeutic while the other is inactive or even harmful.

  • Chiral centers create stereoisomers - most commonly a carbon with four different substituents.