preloader
post-thumb

Last Update: October 5, 2025


BYauthor-thumberic


Keywords

The Problem

I was building a bond yield calculator that needed to display complex mathematical formulas on the web. The page was supposed to render beautiful LaTeX-style equations using MathJax, but instead, visitors saw raw LaTeX code: literal dollar signs, backslashes, and \frac commands sprawled across the screen.

The formula that should have appeared as an elegant fraction looked like this instead:


Yield (%)=Annual Coupon+(Face ValuePrice)Years to MaturityFace Value+Price2×100\text{Yield (\%)} = \frac{\text{Annual Coupon} + \frac{(\text{Face Value} - \text{Price})}{\text{Years to Maturity}}}{\frac{\text{Face Value} + \text{Price}}{2}} \times 100

However, what actually showed up was:

[\ \text{Yield (\%)} = \dfrac{\text{Annual Coupon} + \frac{(\text{Face Value} - \text{Price})}{\text{Years to Maturity}}}{\frac{\text{Face Value} + \text{Price}}{2}} \times 100 \]

Not exactly user-friendly for someone trying to understand bond mathematics.

To debug this, it is common that we check the following:

  1. Ensure the MathJax library is correctly loaded in the HTML <head>.
  2. Verify the MathJax configuration for delimiters matches the LaTeX syntax used.
  3. Confirm that the LaTeX code itself is valid and properly escaped.

It would be easy but time-consuming to manually check each of these steps. Instead, I decided to enlist the help of AI. Why waste time when an AI coding assistant might quickly identify the issue?

The Contestants

I decided to test three leading AI coding assistants on this real-world debugging challenge:

  1. Google Gemini - The AI that originally generated the problematic code
  2. OpenAI Codex - The engine behind GitHub Copilot
  3. Claude Code - Anthropic's CLI-based coding assistant

Each AI was given the same task: fix the MathJax rendering so formulas display properly.

Round 1: Google Gemini

Status: Failed (in a few attempts)

Gemini was the first AI I turned to. Here's how our interaction unfolded:

Attempt 1: Simplifying the Configuration

Gemini's first move was to simplify the MathJax configuration. The original code had a complex, custom script to find and render the formulas. Gemini replaced it with a more standard, simple configuration.

The change:

diff
-    <script>
-        window.MathJax = {
-            tex: {
-                inlineMath: [["\\(", "\\)"]],
-                displayMath: [["\\[", "\\]"]],
-                processEscapes: true
-            },
-            options: {
-                renderActions: {
-                    find_script_mathtex: [10, (doc) => {
-                        for (const node of document.querySelectorAll('script[type^="math/tex"]')) {
-                            const display = !!node.type.match(/; *mode=display/);
-                            const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display);
-                            const text = document.createTextNode('');
-                            node.parentNode.replaceChild(text, node);
-                            math.start = {node: text, delim: '', n: 0};
-                            math.end = {node: text, delim: '', n: 0};
-                            doc.math.push(math);
-                        }
-                    }, '']
-                }
-            }
-        };
-    </script>
+    <script>
+        window.MathJax = {
+            tex: {
+                inlineMath: [['$', '$'], ['\\(', '\\)']],
+                displayMath: [['$$', '$$'], ['\\[', '\\]']]
+            }
+        };
+    </script>

The result: The formula was still not rendering.

Attempt 2: Reverting and Adapting

Since the simple configuration didn't work, Gemini hypothesized that the original complex configuration was there for a reason. It reverted the configuration and, noticing that the script was designed to find formulas in <script> tags, it moved the LaTeX formula from a <p> tag into a <script type="math/tex"> tag.

The change:

diff
-                    <p class="mb-0">
-                        \[
-                        \text{Yield (\%)} = ...
-                        \]
-                    </p>
+                    <script type="math/tex; mode=display">
+                        \text{Yield (\%)} = ...
+                    </script>

The result: Still no good, and the formula was now completely blank. The custom script was removing the formula but failing to render it.

Round 2: OpenAI Codex

Status: Failed

Codex took a different approach. Here is what we actually changed while trying to debug MathJax together:

Attempt 1: Swap defer for async

Codex thought the math wasn't rendering because the MathJax script was waiting too long to execute. It asked me to replace defer with async so the browser would execute the script as soon as it downloaded.

The change:

diff
-    <script defer src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"
+    <script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"
             integrity="sha384-ypRjVNaE7p/Csq3wBECfp0o3lsRoiQFYbUrxWm8m6PHcbVBykedkOSi0PFo8SYK0"
             crossorigin="anonymous"></script>

Result: The script now loaded earlier, but MathJax still refused to typeset anything.

Attempt 2: Rewrite the configuration

When the timing tweak failed, Codex proposed a brand-new configuration object with more explicit loader instructions and different delimiters, hoping MathJax would finally detect the LaTeX.

The change:

diff
-        window.MathJax = {
-            tex: {
-                inlineMath: [["\\(", "\\)"]],
-                displayMath: [["\\[", "\\]"]],
-                processEscapes: true
-            },
-            options: {
-                renderActions: {
-                    find_script_mathtex: [10, (doc) => {
-                        // custom DOM walk removed for brevity
-                    }, '']
-                }
-            }
-        };
+        window.MathJax = {
+            loader: {load: ['input/tex', 'output/chtml']},
+            tex: {
+                inlineMath: [['$', '$'], ['\\(', '\\)']],
+                displayMath: [['$$', '$$'], ['\\[', '\\]']]
+            },
+            startup: {
+                typeset: true,
+                ready: () => {
+                    window.MathJax.startup.defaultReady();
+                }
+            }
+        };

Result: Still nothing. The page loaded faster, but the formulas stayed as plain text.

Attempt 3: Force a manual re-typeset

Codex doubled down by asking me to trigger MathJax.typesetPromise() after the page rendered, just in case the automatic startup hook was being skipped.

The change:

diff
@@
     document.addEventListener('DOMContentLoaded', () => {
-        // earlier script simply relied on MathJax startup
+        if (window.MathJax && window.MathJax.typesetPromise) {
+            window.MathJax.typesetPromise();
+        }
     });

Result: No helper message, no console errors—and still no rendered math.

While the suggestions were technically reasonable, none solved the rendering issue. Codex kept nudging configuration tweaks rather than diagnosing the real problem: how the MathJax library was being loaded and initialized in the first place.

Round 3: Claude Code

Status: Success

Claude Code took a methodical approach:

  1. First attempt: Changed the LaTeX delimiters from \[ and \] to $$, and replaced \dfrac with \frac for consistency
  2. When I reported it still wasn't working: Claude immediately identified the real issue

Here's what Claude Code changed in the script loading:

Before:

html
<script defer src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"
        integrity="sha384-ypRjVNaE7p/Csq3wBECfp0o3lsRoiQFYbUrxWm8m6PHcbVBykedkOSi0PFo8SYK0"
        crossorigin="anonymous"></script>

After:

html
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>

The fix was elegant and precise:

  • Removed the defer attribute that was causing timing issues
  • Removed the integrity check that might have been blocking the script
  • Added a proper startup configuration to ensure MathJax initializes correctly

Result: The formula now renders beautifully as an actual mathematical expression.

Why Claude Code Won

Three key differences set Claude Code apart:

1. Iterative Problem-Solving

When the first solution didn't work, Claude didn't just offer more guesses. It recognized that the problem was deeper than syntax and immediately pivoted to examining the script loading mechanism.

2. Root Cause Analysis

Instead of suggesting configuration tweaks, Claude identified that the defer attribute and integrity constraints were preventing MathJax from loading properly. This showed actual understanding of how browser script loading works.

3. Minimal, Focused Changes

Claude made only the necessary changes. No over-engineering, no adding unnecessary complexity—just removing the attributes that were causing the problem.

The Broader Lesson

This wasn't just about fixing a MathJax related bug. It revealed something important about AI coding assistants:

Confidence ≠ Correctness

All three AIs responded with confidence. Gemini defended its original implementation. Codex offered multiple "solutions." But only Claude Code actually fixed the problem.

The difference? Claude Code demonstrated:

  • Better diagnostic reasoning
  • Willingness to change approach when initial solutions failed
  • Understanding of browser behavior, not just code syntax

Practical Takeaways for Developers

  1. Don't trust the first AI-generated solution - Even if it looks correct, test it thoroughly
  2. Choose AI tools that iterate - The best AI assistants adapt when things don't work
  3. Value diagnosis over guessing - An AI that understands why something is broken is more valuable than one that just suggests fixes
  4. Test with real scenarios - This bug only revealed itself in actual browser rendering

The Technical Details

For those interested, the core issue was:

  1. The defer attribute delayed MathJax loading until after DOM parsing
  2. The integrity hash might have been outdated or mismatched
  3. The MathJax configuration needed to be loaded before the library, and the library needed to load synchronously to properly initialize

Claude Code recognized that the solution wasn't in the configuration—it was in how the script was loaded.

Conclusion

In the battle of AI coding assistants, Claude Code didn't just win—it was the only one that actually solved the problem. While Gemini and Codex offered plausible-sounding advice, Claude Code demonstrated genuine debugging capability.

For developers building production applications, this distinction matters. You need an AI assistant that can not only write code but also troubleshoot real problems when things inevitably go wrong.

Winner: Claude Code Lesson Learned: In AI-assisted development, actual problem-solving beats confident suggestions every time.

Further Reading

For further reading, check out this article on how to use Math Equations and LaTex Expressions in MDX Documents with Next.js.

Previous Article
post-thumb

Oct 03, 2021

Setting up Ingress for a Web Service in a Kubernetes Cluster with NGINX Ingress Controller

A simple tutorial that helps configure ingress for a web service inside a kubernetes cluster using NGINX Ingress Controller

Next Article
post-thumb

Oct 04, 2025

Empowering Open Source Development with AI: My Experience Fixing cloudflare-cli

How Claude Code helped me debug and fix critical bugs in cloudflare-cli, transforming a broken tool into a reliable DNS management solution in minutes.

agico

We transform visions into reality. We specializes in crafting digital experiences that captivate, engage, and innovate. With a fusion of creativity and expertise, we bring your ideas to life, one pixel at a time. Let's build the future together.

Copyright ©  2025  TYO Lab