index.html (44637B)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 7 <!-- ========== PRIMARY SEO ========== --> 8 <title>Pytml – Python + HTML in the Browser</title> 9 <meta name="description" content="Pytml 2.5 is a zero-config JavaScript bridge for Python + HTML, powered by Pyodide 0.314.0.7. Use normal Python, interact with HTML, and build demos and educational pages without a backend."> 10 <meta name="keywords" content="Pytml, Python in browser, Python HTML, Pyodide, DOM, educational coding, static site Python, micropip"> 11 <meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large"> 12 <meta name="author" content="Aswanth R (NodeX-AR)"> 13 <link rel="canonical" href="https://pytml.js.org/"> 14 <link rel="sitemap" type="application/xml" title="Sitemap" href="https://pytml.js.org/sitemap.xml" /> 15 <meta name="google-site-verification" content="ri10nA5CuSxwYGnZIdeVOJl2eh8FayQ2ydsNIrGJTo8" /> 16 17 <!-- ========== FAVICON ========== --> 18 <link rel="icon" href="/favicon.ico" sizes="any"> 19 <link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16"> 20 <link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32"> 21 <link rel="icon" type="image/png" href="/favicon-48x48.png" sizes="48x48"> 22 <link rel="manifest" href="/site.webmanifest"> 23 <link rel="icon" type="image/png" href="/android-chrome-192x192.png" sizes="192x192"> 24 <link rel="icon" type="image/png" href="/android-chrome-512x512.png" sizes="512x512"> 25 <link rel="apple-touch-icon" href="/apple-touch-icon.png"> 26 <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3b82f6"> 27 <link rel="icon" href="/favicon.svg" type="image/svg+xml"> 28 29 <!-- ========== OPEN GRAPH ========== --> 30 <meta property="og:type" content="website"> 31 <meta property="og:title" content="Pytml – Python + HTML in the Browser (vs PyScript & Flask)"> 32 <meta property="og:description" content="Run Python in HTML with zero config. Compare with PyScript & Flask – 15KB, no server, instant setup. Honest, fast, lazy."> 33 <meta property="og:url" content="https://pytml.js.org/"> 34 <meta property="og:image" content="https://pytml.js.org/preview.png"> 35 <meta property="og:image:width" content="1200"> 36 <meta property="og:image:height" content="630"> 37 <meta property="og:site_name" content="NodeX-AR"> 38 <meta property="og:locale" content="en_US"> 39 <meta property="og:updated_time" content="2026-06-18T10:00:00+00:00"> 40 41 <!-- ========== TWITTER CARD ========== --> 42 <meta name="twitter:card" content="summary_large_image"> 43 <meta name="twitter:title" content="Pytml – Python + HTML in the Browser (vs PyScript & Flask)"> 44 <meta name="twitter:description" content="Zero‑config Python in HTML – compare vs PyScript & Flask. 15KB, faster, simpler, no backend."> 45 <meta name="twitter:image" content="https://pytml.js.org/preview.png"> 46 47 <!-- ========== ENHANCED SCHEMA.ORG WITH ZENODO CITATION ========== --> 48 <script type="application/ld+json"> 49 { 50 "@context": "https://schema.org", 51 "@graph": [ 52 { 53 "@type": "WebApplication", 54 "@id": "https://pytml.js.org/#webapp", 55 "mainEntityOfPage": { 56 "@type": "WebPage", 57 "@id": "https://pytml.js.org/" 58 }, 59 "name": "Pytml", 60 "alternateName": "Python in HTML Bridge", 61 "headline": "Pytml 2.5 – Python + HTML in the Browser", 62 "description": "Pytml 2.5 is a zero-config JavaScript bridge that runs Python in HTML using Pyodide and WebAssembly. It adds DOM interaction, browser input, numbered inline UI controls, events and micropip-backed package support.", 63 "keywords": ["Pytml", "PyScript alternative", "Flask alternative", "Python in Browser", "Zero Config Python"], 64 "applicationCategory": "DeveloperApplication", 65 "softwareVersion": "2.5.1", 66 "datePublished": "2026-06-04", 67 "dateModified": "2026-10-02", 68 "license": "https://github.com/NodeX-AR/Pytml/blob/main/LICENSE", 69 "url": "https://pytml.js.org/", 70 "author": { 71 "@type": "Person", 72 "@id": "https://a-r.is-a.dev/#person", 73 "name": "Aswanth R", 74 "alternateName": "NodeX-AR", 75 "url": "https://a-r.is-a.dev/", 76 "sameAs": [ 77 "https://www.wikidata.org/wiki/Q140290370", 78 "https://orcid.org/0009-0007-1619-374X", 79 "https://github.com/NodeX-AR", 80 "https://www.linkedin.com/in/nodex-ar" 81 ] 82 }, 83 "citation": { 84 "@type": "ScholarlyArticle", 85 "@id": "https://doi.org/10.5281/zenodo.20786716", 86 "name": "Pytml: Running Python in the Browser Using Pyodide", 87 "description": "Academic paper describing Pytml, a JavaScript library that uses Pyodide to run Python in web browsers.", 88 "author": { 89 "@type": "Person", 90 "@id": "https://a-r.is-a.dev/#person", 91 "name": "Aswanth R", 92 "url": "https://a-r.is-a.dev/" 93 }, 94 "datePublished": "2026-06-21", 95 "publisher": { 96 "@type": "Organization", 97 "name": "Zenodo" 98 }, 99 "url": "https://doi.org/10.5281/zenodo.20786716" 100 }, 101 "offers": { 102 "@type": "Offer", 103 "price": "0", 104 "priceCurrency": "USD", 105 "availability": "https://schema.org/InStock" 106 }, 107 "sameAs": [ 108 "https://github.com/NodeX-AR/Pytml", 109 "https://dev.to/nodex-ar/pytml-running-python-in-the-browser-without-a-backend-1nn4", 110 "https://devpost.com/software/pytml", 111 "https://www.npmjs.com/package/pytml" 112 ] 113 }, 114 { 115 "@type": "Person", 116 "@id": "https://a-r.is-a.dev/#person", 117 "name": "Aswanth R", 118 "alternateName": "NodeX-AR", 119 "description": "Ethical hacker and Python developer. Creator of Pytml — a zero-config JavaScript library that runs Python in the browser.", 120 "url": "https://a-r.is-a.dev/", 121 "sameAs": [ 122 "https://www.wikidata.org/wiki/Q140290370", 123 "https://orcid.org/0009-0007-1619-374X", 124 "https://github.com/NodeX-AR", 125 "https://www.linkedin.com/in/nodex-ar" 126 ], 127 "citation": { 128 "@type": "ScholarlyArticle", 129 "@id": "https://doi.org/10.5281/zenodo.20786716", 130 "name": "Pytml: Running Python in the Browser Using Pyodide", 131 "datePublished": "2026-06-21", 132 "publisher": "Zenodo", 133 "url": "https://doi.org/10.5281/zenodo.20786716" 134 } 135 }, 136 { 137 "@type": "ScholarlyArticle", 138 "@id": "https://doi.org/10.5281/zenodo.20786716", 139 "name": "Pytml: Running Python in the Browser Using Pyodide", 140 "description": "Academic paper describing Pytml, a JavaScript library that uses Pyodide to run Python in web browsers.", 141 "author": { 142 "@type": "Person", 143 "@id": "https://a-r.is-a.dev/#person", 144 "name": "Aswanth R", 145 "url": "https://a-r.is-a.dev/" 146 }, 147 "datePublished": "2026-06-21", 148 "publisher": { 149 "@type": "Organization", 150 "name": "Zenodo" 151 }, 152 "url": "https://doi.org/10.5281/zenodo.20786716", 153 "isPartOf": { 154 "@type": "CreativeWork", 155 "name": "Zenodo", 156 "url": "https://zenodo.org/" 157 } 158 }, 159 { 160 "@type": "BreadcrumbList", 161 "itemListElement": [ 162 { 163 "@type": "ListItem", 164 "position": 1, 165 "name": "Home", 166 "item": "https://pytml.js.org/" 167 }, 168 { 169 "@type": "ListItem", 170 "position": 2, 171 "name": "Pytml vs PyScript & Flask" 172 } 173 ] 174 }, 175 { 176 "@type": "FAQPage", 177 "mainEntity": [ 178 { 179 "@type": "Question", 180 "name": "What is the actual bundle size of Pytml?", 181 "acceptedAnswer": { 182 "@type": "Answer", 183 "text": "Pytml loads its JavaScript bridge immediately and loads Pyodide 0.314.0.7 from the configured CDN during startup. The exact transfer size varies with the browser and cache state." 184 } 185 }, 186 { 187 "@type": "Question", 188 "name": "Can I use Pytml with React, Vue, or other JS frameworks?", 189 "acceptedAnswer": { 190 "@type": "Answer", 191 "text": "Yes. Pytml is framework-agnostic. You can drop the script tag in any HTML page, including those generated by React or Vue. It scans the DOM for <py> tags after the framework renders." 192 } 193 }, 194 { 195 "@type": "Question", 196 "name": "What Python packages are supported?", 197 "acceptedAnswer": { 198 "@type": "Answer", 199 "text": "Pytml uses Pyodide's package environment and micropip workflow. Packages available in the Pyodide distribution, including NumPy, pandas and Matplotlib, can be used when compatible with the browser runtime." 200 } 201 }, 202 { 203 "@type": "Question", 204 "name": "How does Pytml handle errors and debugging?", 205 "acceptedAnswer": { 206 "@type": "Answer", 207 "text": "Python tracebacks are captured and rendered directly in the browser terminal box. Syntax errors, runtime exceptions, and import errors all appear with line numbers and stack traces, just like a local Python REPL." 208 } 209 }, 210 { 211 "@type": "Question", 212 "name": "Is there any server-side component or data sent to a backend?", 213 "acceptedAnswer": { 214 "@type": "Answer", 215 "text": "No. Everything runs client-side in WebAssembly. No Python code, input, or output ever leaves your browser. This makes Pytml ideal for privacy-sensitive demos and educational tools." 216 } 217 }, 218 { 219 "@type": "Question", 220 "name": "What are the performance limitations?", 221 "acceptedAnswer": { 222 "@type": "Answer", 223 "text": "Pytml runs Python in a WebAssembly sandbox. It's roughly 2-5x slower than native CPython for CPU-bound tasks, and memory is limited to the browser's tab allocation (usually 2-4GB). Heavy number crunching may be slower than server-side Python, but for demos, data viz, and scripting, it's perfectly fine." 224 } 225 } 226 ] 227 } 228 ] 229 } 230 </script> 231 232 <!-- ========== FONTS ========== --> 233 <link rel="preconnect" href="https://fonts.googleapis.com"> 234 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 235 <link href="https://fonts.googleapis.com/css2?family=Inter:opsz@14..32&family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet"> 236 237 <!-- ========== STYLES (YOUR ORIGINAL – UNCHANGED, except minor mobile fixes below) ========== --> 238 <script src="https://cdn.tailwindcss.com"></script> 239 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> 240 241 <style> 242 :root { 243 --bg: #050505; 244 --accent: #3b82f6; 245 --accent-dark: #2563eb; 246 --accent-glow: rgba(59, 130, 246, 0.2); 247 --border: rgba(255,255,255,0.08); 248 --text: #e5e5e5; 249 } 250 * { margin: 0; padding: 0; box-sizing: border-box; } 251 body { 252 background: var(--bg); 253 color: var(--text); 254 font-family: 'Inter', system-ui, sans-serif; 255 overflow-x: hidden; 256 -webkit-tap-highlight-color: transparent; 257 } 258 h1, h2, h3 { font-family: 'Space Grotesk', sans-serif; } 259 .mono { font-family: 'JetBrains Mono', monospace; } 260 .text-accent { color: var(--accent); } 261 .border-accent { border-color: var(--accent); } 262 .font-heading { font-family: 'Space Grotesk', sans-serif; } 263 264 .bg-noise { 265 position: fixed; 266 inset: 0; 267 pointer-events: none; 268 z-index: 9998; 269 opacity: 0.03; 270 background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.7' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E"); 271 } 272 273 .btn-term { 274 display: inline-flex; 275 align-items: center; 276 gap: 0.5rem; 277 padding: 0.7rem 1.3rem; 278 background: #0c0c0c; 279 border: 1px solid #27272a; 280 color: #b0b0b0; 281 font-family: 'JetBrains Mono', monospace; 282 font-size: 0.85rem; 283 text-transform: uppercase; 284 transition: all 0.2s; 285 text-decoration: none; 286 touch-action: manipulation; 287 cursor: pointer; 288 min-height: 48px; 289 line-height: 1.2; 290 } 291 .btn-term::before { content: "> "; margin-right: 4px; color: var(--accent); } 292 .btn-term:hover { background: var(--accent); color: #050505; border-color: var(--accent); transform: translateX(4px); } 293 .btn-term:active { transform: scale(0.97); } 294 295 .card { 296 background: rgba(8,8,8,0.7); 297 backdrop-filter: blur(2px); 298 border: 1px solid #1e1e1e; 299 border-radius: 0.5rem; 300 padding: 1.5rem; 301 transition: border 0.25s, transform 0.25s; 302 height: 100%; 303 } 304 .card:hover { border-color: var(--accent); transform: translateY(-4px); background: rgba(59,130,246,0.02); } 305 306 .code-box { 307 background: #0b0b0b; 308 border-left: 3px solid var(--accent); 309 padding: 1rem; 310 font-family: 'JetBrains Mono', monospace; 311 font-size: 0.85rem; 312 overflow-x: auto; 313 white-space: pre-wrap; 314 word-break: break-word; 315 color: #c0c0c0; 316 border-radius: 0 0.5rem 0.5rem 0; 317 max-width: 100%; 318 tab-size: 2; 319 line-height: 1.6; 320 } 321 322 .fade-up { opacity: 0; transform: translateY(30px); transition: all 0.7s cubic-bezier(0.2, 0.9, 0.4, 1); } 323 .fade-up.visible { opacity: 1; transform: translateY(0); } 324 325 .logo-box { 326 display: inline-flex; 327 align-items: center; 328 gap: 0.75rem; 329 background: #0c0c0c; 330 border: 2px solid var(--accent); 331 padding: 0.4rem 1rem 0.4rem 0.75rem; 332 border-radius: 0.25rem; 333 box-shadow: 0 0 20px var(--accent-glow); 334 flex-wrap: nowrap; 335 max-width: 100%; 336 } 337 .logo-box img { 338 width: 42px; 339 height: 42px; 340 display: block; 341 flex-shrink: 0; 342 } 343 .logo-box .text { 344 font-family: 'JetBrains Mono', monospace; 345 font-size: 2rem; 346 font-weight: 700; 347 color: var(--accent); 348 letter-spacing: 0.08em; 349 line-height: 1.1; 350 white-space: nowrap; 351 } 352 .logo-box .text small { 353 font-size: 0.6rem; 354 color: #5A627A; 355 display: block; 356 letter-spacing: 0.2em; 357 margin-top: 0.05rem; 358 white-space: nowrap; 359 } 360 361 .badge-term { 362 display: inline-block; 363 background: #0c0c0c; 364 border: 1px solid #27272a; 365 padding: 0.2rem 0.8rem; 366 font-family: 'JetBrains Mono', monospace; 367 font-size: 0.7rem; 368 color: var(--text); 369 border-radius: 999px; 370 } 371 .badge-term i { color: var(--accent); } 372 373 .skill-tag { 374 background: #0f0f0f; 375 border: 1px solid #262626; 376 padding: 0.25rem 0.8rem; 377 font-size: 0.75rem; 378 font-family: monospace; 379 border-radius: 999px; 380 transition: all 0.2s; 381 white-space: nowrap; 382 } 383 .skill-tag:hover { border-color: var(--accent); color: var(--accent); } 384 385 .footer-link { color: #5A627A; text-decoration: none; transition: color 0.2s; } 386 .footer-link:hover { color: var(--accent); text-decoration: underline; } 387 388 .marquee { 389 overflow: hidden; 390 mask-image: linear-gradient(to right, transparent, black 10%, black 90%, transparent); 391 -webkit-mask-image: linear-gradient(to right, transparent, black 10%, black 90%, transparent); 392 } 393 .marquee-track { 394 display: flex; 395 gap: 2rem; 396 animation: marqueeScroll 25s linear infinite; 397 width: max-content; 398 } 399 @keyframes marqueeScroll { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } } 400 401 ::-webkit-scrollbar { width: 6px; } 402 ::-webkit-scrollbar-track { background: #0a0a0a; } 403 ::-webkit-scrollbar-thumb { background: #1f1f1f; border-radius: 0; } 404 ::-webkit-scrollbar-thumb:hover { background: var(--accent); } 405 406 .pulse-ring { 407 animation: pulse 2s ease-in-out infinite; 408 } 409 @keyframes pulse { 410 0% { transform: scale(1); opacity: 0.15; } 411 50% { transform: scale(1.1); opacity: 0.35; } 412 100% { transform: scale(1); opacity: 0.15; } 413 } 414 415 /* ===== YOUR MOBILE‑FIRST TWEAKS (ENHANCED FOR BETTER CENTERING & SCROLL) ===== */ 416 @media (max-width: 640px) { 417 .logo-box { padding: 0.3rem 0.7rem 0.3rem 0.5rem; gap: 0.5rem; } 418 .logo-box img { width: 32px; height: 32px; } 419 .logo-box .text { font-size: 1.5rem; letter-spacing: 0.05em; } 420 .logo-box .text small { font-size: 0.5rem; } 421 .btn-term { font-size: 0.7rem; padding: 0.5rem 0.9rem; min-height: 44px; gap: 0.35rem; } 422 .btn-term::before { content: ">"; margin-right: 2px; } 423 .card { padding: 1rem; } 424 .code-box { font-size: 0.75rem; padding: 0.75rem; } 425 .hero-img-wrap img { width: 120px; height: 120px; } 426 .hero-img-wrap .pulse-ring { width: 160px; height: 160px; } 427 .marquee-track { gap: 1rem; } 428 .skill-tag { font-size: 0.65rem; padding: 0.2rem 0.6rem; } 429 430 /* FIX: Comparison table scroll on mobile */ 431 .compare-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; } 432 .compare-table { min-width: 420px; } 433 } 434 @media (max-width: 480px) { 435 .logo-box .text { font-size: 1.25rem; } 436 .logo-box img { width: 28px; height: 28px; } 437 .logo-box .text small { font-size: 0.45rem; } 438 .btn-term { font-size: 0.65rem; padding: 0.4rem 0.7rem; min-height: 40px; } 439 .hero-img-wrap img { width: 100px; height: 100px; } 440 .hero-img-wrap .pulse-ring { width: 140px; height: 140px; } 441 .code-box { font-size: 0.65rem; padding: 0.5rem; } 442 .badge-term { font-size: 0.6rem; padding: 0.15rem 0.5rem; } 443 .card h3 { font-size: 1rem; } 444 .skill-tag { font-size: 0.55rem; padding: 0.15rem 0.5rem; } 445 .marquee-track { gap: 0.75rem; } 446 } 447 @media (max-width: 380px) { 448 .logo-box .text { font-size: 1rem; } 449 .logo-box img { width: 24px; height: 24px; } 450 .logo-box .text small { font-size: 0.4rem; } 451 .btn-term { font-size: 0.55rem; padding: 0.3rem 0.6rem; min-height: 36px; } 452 .hero-img-wrap img { width: 80px; height: 80px; } 453 .hero-img-wrap .pulse-ring { width: 110px; height: 110px; } 454 .code-box { font-size: 0.6rem; padding: 0.4rem; } 455 /* Tighter padding on ultra-small screens */ 456 section { padding-left: 0.75rem !important; padding-right: 0.75rem !important; } 457 .compare-table { min-width: 360px; font-size: 0.55rem; } 458 .compare-table th, .compare-table td { padding: 0.3rem 0.2rem; } 459 } 460 @media (prefers-reduced-motion: reduce) { 461 .fade-up { opacity: 1; transform: none; transition: none; } 462 .marquee-track { animation: none; } 463 .pulse-ring { animation: none; } 464 .btn-term:hover { transform: none; } 465 .card:hover { transform: none; } 466 } 467 468 /* ===== COMPARISON TABLE ===== */ 469 .compare-table { 470 width: 100%; 471 border-collapse: collapse; 472 font-size: 0.8rem; 473 text-align: left; 474 } 475 .compare-table th, 476 .compare-table td { 477 padding: 0.75rem 0.5rem; 478 border-bottom: 1px solid #1e1e1e; 479 } 480 .compare-table th { 481 color: var(--accent); 482 font-family: 'JetBrains Mono', monospace; 483 font-weight: 600; 484 text-transform: uppercase; 485 letter-spacing: 0.05em; 486 font-size: 0.7rem; 487 } 488 .compare-table td { 489 color: #b0b0b0; 490 } 491 .compare-table .highlight { 492 color: var(--text); 493 font-weight: 500; 494 } 495 .compare-table .check { color: var(--accent); } 496 .compare-table .cross { color: #555; } 497 @media (max-width: 640px) { 498 .compare-table th, 499 .compare-table td { 500 padding: 0.5rem 0.3rem; 501 font-size: 0.65rem; 502 } 503 .compare-table th { font-size: 0.6rem; } 504 } 505 </style> 506 </head> 507 <body> 508 509 <div class="bg-noise"></div> 510 511 <!-- ===== HEADER WITH LOGO (ONLY ONE – KEPT THIS) ===== --> 512 <header class="relative z-10 border-b border-white/5 py-4 px-4 sm:px-6"> 513 <div class="max-w-6xl mx-auto flex items-center justify-between flex-wrap gap-4"> 514 <div class="logo-box"> 515 <img src="/android-chrome-192x192.png" alt="Pytml logo" width="42" height="42" loading="lazy"> 516 <div class="text"> 517 PYTML 518 <small>python → html</small> 519 </div> 520 </div> 521 <nav aria-label="Primary navigation"> 522 <ul class="flex flex-wrap gap-4 text-sm mono text-gray-400"> 523 <li><a href="#compare" class="hover:text-accent transition">Comparison</a></li> 524 <li><a href="#features" class="hover:text-accent transition">Features</a></li> 525 <li><a href="#code" class="hover:text-accent transition">Code</a></li> 526 <li><a href="#faq" class="hover:text-accent transition">FAQ</a></li> 527 <li><a href="demo.html" class="hover:text-accent transition">Demos</a></li> 528 <li><a href="https://github.com/NodeX-AR/Pytml" target="_blank" rel="noopener noreferrer" class="hover:text-accent transition">GitHub</a></li> 529 </ul> 530 </nav> 531 </div> 532 </header> 533 534 <!-- ===== MAIN CONTENT ===== --> 535 <main class="relative z-10"> 536 537 <!-- ===== HERO (FIXED CENTERING) ===== --> 538 <section class="min-h-screen flex items-center justify-center px-4 py-12 sm:px-6 sm:py-20"> 539 <div class="max-w-6xl mx-auto w-full"> 540 <div class="flex flex-col md:flex-row items-center gap-6 md:gap-12"> 541 <div class="flex-1 text-center md:text-left"> 542 <div class="fade-up mt-3 md:mt-4"> 543 <h1 class="text-base sm:text-xl md:text-2xl text-gray-300 mono">$> Run Python in your browser – no backend</h1> 544 </div> 545 <div class="fade-up mt-2"> 546 <span class="badge-term"><i class="fas fa-bolt text-accent mr-1"></i> Python + HTML with Pyodide</span> 547 </div> 548 <div class="fade-up mt-3"> 549 <p class="text-gray-400 max-w-lg mx-auto md:mx-0 leading-relaxed text-sm sm:text-base"> 550 A zero-config <strong>Python + HTML runtime</strong> that lets you embed <strong>Python code</strong> directly in your <strong>HTML</strong>. 551 Handles <code class="text-accent">print()</code>, <code class="text-accent">input()</code>, and errors – live, in the browser. 552 <span class="block mt-1 text-xs text-gray-500">No server. No heavy frameworks. Just <code class="text-accent"><py></code> and go.</span> 553 </p> 554 </div> 555 <div class="fade-up mt-5 flex flex-wrap gap-2 sm:gap-3 justify-center md:justify-start"> 556 <a href="demo.html" target="_blank" class="btn-term"><i class="fas fa-play"></i> Live Demo</a> 557 <a href="#compare" class="btn-term"><i class="fas fa-balance-scale"></i> Compare</a> 558 <a href="#code" class="btn-term"><i class="fas fa-code"></i> See Code</a> 559 <a href="https://github.com/NodeX-AR/Pytml" target="_blank" class="btn-term"><i class="fab fa-github"></i> GitHub</a> 560 </div> 561 <div class="fade-up mt-4 flex flex-wrap gap-2 justify-center md:justify-start"> 562 <span class="badge-term"><i class="fas fa-cubes mr-1"></i> Pytml 2.5</span> 563 </div> 564 </div> 565 <!-- FIXED: added items-center and mx-auto to ensure dead-center on mobile --> 566 <div class="flex-1 flex justify-center items-center hero-img-wrap"> 567 <div class="relative w-40 h-40 sm:w-60 sm:h-60 md:w-72 md:h-72 flex items-center justify-center mx-auto"> 568 <div class="absolute inset-0 rounded-full border border-[var(--accent)] opacity-20 pulse-ring"></div> 569 <img src="/android-chrome-192x192.png" alt="Pytml" class="w-28 h-28 sm:w-40 sm:h-40 md:w-56 md:h-56 object-contain" style="filter: drop-shadow(0 0 30px rgba(59,130,246,0.2));" loading="lazy"> 570 </div> 571 </div> 572 </div> 573 </div> 574 </section> 575 576 <!-- ===== WHAT IS PYTML? (unchanged) ===== --> 577 <section class="py-16 sm:py-20 px-4 sm:px-6 border-t border-white/5"> 578 <div class="max-w-5xl mx-auto"> 579 <div class="fade-up text-center"> 580 <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold font-heading mb-3"><span class="text-accent">//</span> The Lazy Bridge</h2> 581 </div> 582 <div class="grid sm:grid-cols-2 gap-6 sm:gap-8 mt-8"> 583 <div class="fade-up space-y-4 text-gray-300 leading-relaxed text-sm sm:text-base"> 584 <p><strong>Pytml</strong> is a minimal <strong>JavaScript library</strong> that lets you write <span class="text-accent font-bold">Python inside your HTML</span> – no server, no setup.</p> 585 <p>It wraps <span class="text-accent">Pyodide</span> (Python compiled to <strong>WebAssembly</strong>) and connects Python to HTML, including <code class="text-accent">print()</code>, browser-backed <code class="text-accent">input()</code>, DOM access, events and numbered inline controls.</p> 586 <p>Built by <span class="text-accent">Aswanth R (NodeX‑AR)</span> – a Class 12 student who wanted a easy and fast way to show <strong>Python projects</strong> live on the web.</p> 587 </div> 588 <div class="fade-up card"> 589 <h3 class="text-base sm:text-lg font-bold font-heading mb-2">Why it exists</h3> 590 <ul class="space-y-2 text-gray-400 text-xs sm:text-sm mono"> 591 <li><span class="text-accent">➜</span> No Flask, no Django, no server</li> 592 <li><span class="text-accent">➜</span> Just <span class="text-white"><py>code</py></span> or <span class="text-white"><py1>...</py1></span></li> 593 <li><span class="text-accent">➜</span> Works offline after first load</li> 594 <li><span class="text-accent">➜</span> Lightweight JavaScript bridge</li> 595 </ul> 596 </div> 597 </div> 598 </div> 599 </section> 600 601 <!-- ===== HONEST COMPARISON (FIXED MOBILE SCROLL) ===== --> 602 <section id="compare" class="py-16 sm:py-20 px-4 sm:px-6 border-t border-white/5"> 603 <div class="max-w-5xl mx-auto"> 604 <div class="fade-up text-center"> 605 <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold font-heading mb-3"><span class="text-accent"></span>Comparison</h2> 606 <p class="text-gray-400 max-w-2xl mx-auto text-sm sm:text-base">Just real trade‑offs. Pytml is <strong>not</strong> a Flask replacement for backends, and it's <strong>not</strong> as feature‑heavy as PyScript. But for <strong>static pages, demos, and education</strong>, it wins on speed, size, and simplicity.</p> 607 </div> 608 <div class="fade-up mt-8 card overflow-x-auto compare-table-wrap"> 609 <table class="compare-table min-w-[420px]"> 610 <thead> 611 <tr> 612 <th>Feature</th> 613 <th>Pytml</th> 614 <th>PyScript</th> 615 <th>Flask</th> 616 </tr> 617 </thead> 618 <tbody> 619 <tr> 620 <td class="highlight">Setup time</td> 621 <td><span class="check">✔</span> 1 min (1 tag)</td> 622 <td><span class="cross">~</span> 5 min (config)</td> 623 <td><span class="cross">~</span> 10+ min (env)</td> 624 </tr> 625 <tr> 626 <td class="highlight">Server required</td> 627 <td><span class="check">✔</span> No (static)</td> 628 <td><span class="check">✔</span> No (static)</td> 629 <td><span class="cross">✘</span> Yes (backend)</td> 630 </tr> 631 <tr> 632 <td class="highlight">Wrapper size</td> 633 <td><span class="check">✔</span> <strong>~15 KB</strong></td> 634 <td><span class="cross">~</span> Larger bundle</td> 635 <td><span class="cross">—</span> N/A</td> 636 </tr> 637 <tr> 638 <td class="highlight">Pyodide load</td> 639 <td><span class="check">✔</span> Lazy</td> 640 <td><span class="cross">✘</span> Eager</td> 641 <td><span class="cross">—</span> N/A</td> 642 </tr> 643 <tr> 644 <td class="highlight">Configuration</td> 645 <td><span class="check">✔</span> Zero (<code><py></code>)</td> 646 <td><span class="cross">~</span> <code><py-config></code></td> 647 <td><span class="cross">~</span> Full structure</td> 648 </tr> 649 <tr> 650 <td class="highlight">Best for</td> 651 <td><span class="check">✔</span> Demos, education</td> 652 <td><span class="check">✔</span> Complex browser apps</td> 653 <td><span class="check">✔</span> Full‑stack apps</td> 654 </tr> 655 </tbody> 656 </table> 657 <p class="text-xs text-gray-500 mt-4 text-center">* Pyodide (~6MB) loaded from CDN – same for both. Pytml adds a small JavaScript bridge around Pyodide.</p> 658 </div> 659 </div> 660 </section> 661 662 <!-- ===== HOW IT WORKS (unchanged) ===== --> 663 <section id="code" class="py-16 sm:py-20 px-4 sm:px-6 border-t border-white/5"> 664 <div class="max-w-5xl mx-auto"> 665 <div class="fade-up text-center"> 666 <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold font-heading mb-6"><span class="text-accent">#</span> How It Works</h2> 667 </div> 668 <div class="grid sm:grid-cols-2 gap-6 sm:gap-8"> 669 <div class="fade-up space-y-3 text-gray-300 leading-relaxed mono text-xs sm:text-sm"> 670 <div class="flex gap-3"><span class="text-accent font-bold">1.</span> Loads <strong>Pyodide</strong> (WebAssembly) from CDN</div> 671 <div class="flex gap-3"><span class="text-accent font-bold">2.</span> Connects <code>print()</code>, <code>input()</code> and the DOM</div> 672 <div class="flex gap-3"><span class="text-accent font-bold">3.</span> Finds <code><py></code>, <code><py1></code>, <code><py2></code> and other Python blocks</div> 673 <div class="flex gap-3"><span class="text-accent font-bold">4.</span> Runs Python, catches output and errors</div> 674 <div class="flex gap-3"><span class="text-accent font-bold">5.</span> Displays everything in a styled terminal</div> 675 </div> 676 <div class="fade-up code-box"> 677 <!DOCTYPE html> 678 <html> 679 <head> 680 <script src="https://pytml.vercel.app/pytml.js"></script> 681 </head> 682 <body> 683 <py1> 684 <btn1>Hello</btn1> 685 686 def hello(event): 687 print("Hello from Python!") 688 689 btn1.on("click", hello) 690 </py1> 691 </body> 692 </html> 693 </div> 694 </div> 695 </div> 696 </section> 697 698 <!-- ===== FEATURES (your original 4 cards) ===== --> 699 <section id="features" class="py-16 sm:py-20 px-4 sm:px-6 border-t border-white/5"> 700 <div class="max-w-6xl mx-auto"> 701 <div class="fade-up text-center"> 702 <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold font-heading mb-6"><span class="text-accent"></span> Features</h2> 703 </div> 704 <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4"> 705 <div class="fade-up card text-center"><i class="fas fa-bolt text-accent text-2xl mb-2"></i><p class="text-sm font-semibold">Zero config</p><p class="text-xs text-gray-500">Just a script tag</p></div> 706 <div class="fade-up card text-center"><i class="fas fa-terminal text-accent text-2xl mb-2"></i><p class="text-sm font-semibold">Real I/O</p><p class="text-xs text-gray-500">print() and input() work</p></div> 707 <div class="fade-up card text-center"><i class="fas fa-mobile-alt text-accent text-2xl mb-2"></i><p class="text-sm font-semibold">Any device</p><p class="text-xs text-gray-500">Works on phones too</p></div> 708 <div class="fade-up card text-center"><i class="fas fa-code text-accent text-2xl mb-2"></i><p class="text-sm font-semibold">Open source</p><p class="text-xs text-gray-500">Apache 2.0</p></div> 709 </div> 710 </div> 711 </section> 712 713 <!-- ===== USE CASES ===== --> 714 <section class="py-16 sm:py-20 px-4 sm:px-6 border-t border-white/5"> 715 <div class="max-w-6xl mx-auto"> 716 <div class="fade-up text-center"> 717 <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold font-heading mb-3"><span class="text-accent"></span> When to Use Pytml</h2> 718 <p class="text-gray-400 max-w-2xl mx-auto mb-8 text-sm sm:text-base">Be honest – Pytml shines here, not everywhere.</p> 719 </div> 720 <div class="grid grid-cols-1 sm:grid-cols-2 gap-4"> 721 <div class="fade-up card"> 722 <h3 class="text-sm font-bold">Education & Tutorials</h3> 723 <p class="text-xs text-gray-400 mt-1">Embed live Python in courseware. Students run code without installing anything – instant feedback.</p> 724 </div> 725 <div class="fade-up card"> 726 <h3 class="text-sm font-bold">Technical Blogs</h3> 727 <p class="text-xs text-gray-400 mt-1">Interactive code snippets – readers tweak and run Python right in the article. No server costs.</p> 728 </div> 729 <div class="fade-up card"> 730 <h3 class="text-sm font-bold">Data Science Demos</h3> 731 <p class="text-xs text-gray-400 mt-1">Show visualisations or data processing with Python, all client‑side. Perfect for portfolio projects.</p> 732 </div> 733 <div class="fade-up card"> 734 <h3 class="text-sm font-bold">Rapid Prototyping</h3> 735 <p class="text-xs text-gray-400 mt-1">Test Python logic in the browser without spinning up a Flask app. Great for hackathons.</p> 736 </div> 737 <div class="fade-up card sm:col-span-2 text-center border-accent/20"> 738 <p class="text-xs text-gray-400"><span class="text-accent font-bold">Not for:</span> Heavy backend processing, database operations, or production APIs – use Flask for that. Pytml is client‑side only.</p> 739 </div> 740 </div> 741 </div> 742 </section> 743 744 <!-- ===== TECH STACK MARQUEE (unchanged) ===== --> 745 <section class="py-12 sm:py-16 px-4 sm:px-6 border-t border-white/5"> 746 <div class="max-w-6xl mx-auto"> 747 <div class="fade-up text-center"> 748 <h2 class="text-xl sm:text-2xl font-bold font-heading mb-4"><span class="text-accent">🧩</span> Built With</h2> 749 </div> 750 <div class="marquee"> 751 <div class="marquee-track"> 752 <span class="skill-tag"><i class="fab fa-python"></i> Python</span> 753 <span class="skill-tag"><i class="fab fa-js"></i> JavaScript</span> 754 <span class="skill-tag"><i class="fas fa-globe"></i> WebAssembly</span> 755 <span class="skill-tag"><i class="fas fa-database"></i> Pyodide</span> 756 <span class="skill-tag"><i class="fas fa-code"></i> ES6</span> 757 <span class="skill-tag"><i class="fab fa-html5"></i> HTML5</span> 758 <span class="skill-tag"><i class="fas fa-terminal"></i> Bash</span> 759 <span class="skill-tag"><i class="fab fa-github"></i> GitHub</span> 760 <span class="skill-tag"><i class="fas fa-cloud"></i> Vercel</span> 761 <span class="skill-tag"><i class="fas fa-shield-alt"></i> Apache 2.0</span> 762 <span class="skill-tag"><i class="fab fa-python"></i> Python</span> 763 <span class="skill-tag"><i class="fab fa-js"></i> JavaScript</span> 764 <span class="skill-tag"><i class="fas fa-globe"></i> WebAssembly</span> 765 <span class="skill-tag"><i class="fas fa-database"></i> Pyodide</span> 766 <span class="skill-tag"><i class="fas fa-code"></i> ES6</span> 767 <span class="skill-tag"><i class="fab fa-html5"></i> HTML5</span> 768 <span class="skill-tag"><i class="fas fa-terminal"></i> Bash</span> 769 <span class="skill-tag"><i class="fab fa-github"></i> GitHub</span> 770 <span class="skill-tag"><i class="fas fa-cloud"></i> Vercel</span> 771 <span class="skill-tag"><i class="fas fa-shield-alt"></i> Apache 2.0</span> 772 </div> 773 </div> 774 </div> 775 </section> 776 777 <!-- ===== UPDATED FAQ (actually useful) ===== --> 778 <section id="faq" class="py-16 sm:py-20 px-4 sm:px-6 border-t border-white/5"> 779 <div class="max-w-4xl mx-auto"> 780 <div class="fade-up text-center"> 781 <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold font-heading mb-6"><span class="text-accent"></span> Frequently Asked Questions</h2> 782 </div> 783 <div class="space-y-4"> 784 <div class="fade-up card"> 785 <h3 class="text-sm font-bold">What is the actual bundle size of Pytml?</h3> 786 <p class="text-xs text-gray-400 mt-1">The Pytml wrapper itself is <strong>~15KB</strong> minified. The Pyodide runtime (~6MB) is loaded lazily from CDN only when you actually run Python code, so initial page load isn't blocked.</p> 787 </div> 788 <div class="fade-up card"> 789 <h3 class="text-sm font-bold">Can I use Pytml with React, Vue, or other JS frameworks?</h3> 790 <p class="text-xs text-gray-400 mt-1">Yes. Pytml is framework-agnostic. You can drop the script tag in any HTML page, including those generated by React or Vue. It scans the DOM for <code><py></code> tags after the framework renders.</p> 791 </div> 792 <div class="fade-up card"> 793 <h3 class="text-sm font-bold">What Python packages are supported?</h3> 794 <p class="text-xs text-gray-400 mt-1">Any pure-Python package available on Pyodide's repository – including NumPy, Pandas, Matplotlib, SciPy, and scikit-learn. C-extensions are not supported unless compiled to WebAssembly.</p> 795 </div> 796 <div class="fade-up card"> 797 <h3 class="text-sm font-bold">How does Pytml handle errors and debugging?</h3> 798 <p class="text-xs text-gray-400 mt-1">Python tracebacks are captured and rendered directly in the browser terminal box. Syntax errors, runtime exceptions, and import errors all appear with line numbers and stack traces, just like a local Python REPL.</p> 799 </div> 800 <div class="fade-up card"> 801 <h3 class="text-sm font-bold">Is there any server-side component or data sent to a backend?</h3> 802 <p class="text-xs text-gray-400 mt-1">No. Everything runs client-side in WebAssembly. No Python code, input, or output ever leaves your browser. This makes Pytml ideal for privacy-sensitive demos and educational tools.</p> 803 </div> 804 <div class="fade-up card"> 805 <h3 class="text-sm font-bold">What are the performance limitations?</h3> 806 <p class="text-xs text-gray-400 mt-1">Pytml runs Python in a WebAssembly sandbox. It's roughly <strong>2-5x slower</strong> than native CPython for CPU-bound tasks, and memory is limited to the browser's tab allocation (usually 2-4GB). Heavy number crunching may be slower than server-side Python, but for demos, data viz, and scripting, it's perfectly fine.</p> 807 </div> 808 </div> 809 </div> 810 </section> 811 812 <!-- ===== TESTIMONIALS – REMOVED (no fake reviews) ===== --> 813 814 <!-- ===== LIVE DEMO (unchanged) ===== --> 815 <section id="demo" class="py-16 sm:py-20 px-4 sm:px-6 border-t border-white/5"> 816 <div class="max-w-4xl mx-auto text-center"> 817 <div class="fade-up"> 818 <h2 class="text-2xl sm:text-3xl md:text-4xl font-bold font-heading mb-3"><span class="text-accent">[</span> Try It <span class="text-accent">]</span></h2> 819 </div> 820 <div class="fade-up"> 821 <p class="text-gray-300 max-w-xl mx-auto mb-6 sm:mb-8 text-sm sm:text-base">See Pytml in action – run <strong>Python code</strong> live in your browser.</p> 822 </div> 823 <div class="fade-up flex flex-wrap justify-center gap-2 sm:gap-3 md:gap-5"> 824 <a href="demo.html" target="_blank" class="btn-term"><i class="fas fa-globe"></i> Live Demo</a> 825 <a href="https://github.com/NodeX-AR/Pytml" target="_blank" class="btn-term"><i class="fab fa-github"></i> GitHub Repo</a> 826 <a href="https://dev.to/nodex-ar/pytml-running-python-in-the-browser-without-a-backend-1nn4" target="_blank" class="btn-term"><i class="fas fa-pen"></i> DEV Article</a> 827 <a href="https://devpost.com/software/pytml" target="_blank" class="btn-term"><i class="fas fa-trophy"></i> Devpost</a> 828 </div> 829 <div class="fade-up mt-8 sm:mt-10 text-xs sm:text-sm text-gray-500 mono"> 830 Made from <span class="text-accent">Laziness</span> of 831 <a href="https://a-r.is-a.dev" target="_blank" class="text-accent hover:underline">Aswanth R (NodeX-AR)</a> 832 </div> 833 </div> 834 </section> 835 </main> 836 837 <!-- ===== FOOTER ===== --> 838 <footer class="py-6 sm:py-8 px-4 sm:px-6 border-t border-white/5 text-center text-xs text-gray-600"> 839 <p> 840 <a href="https://a-r.is-a.dev" target="_blank" class="footer-link">Aswanth R (NodeX-AR)</a> · 841 <a href="https://github.com/NodeX-AR/Pytml" target="_blank" class="footer-link">GitHub</a> · 842 <a href="demo.html" target="_blank" class="footer-link">Live Demo</a> · 843 <a href="https://github.com/NodeX-AR/Pytml/blob/main/README.md" target="_blank" class="footer-link">Docs</a> 844 </p> 845 <p class="mt-1 text-gray-700">Open-source under Apache 2.0 · 2026</p> 846 </footer> 847 848 <!-- ========== SCRIPTS (unchanged) ========== --> 849 <script> 850 const observer = new IntersectionObserver((entries) => { 851 entries.forEach(entry => { if (entry.isIntersecting) entry.target.classList.add('visible'); }); 852 }, { threshold: 0.1 }); 853 document.querySelectorAll('.fade-up').forEach(el => observer.observe(el)); 854 855 document.querySelector('a[href="#code"]')?.addEventListener('click', (e) => { 856 e.preventDefault(); 857 document.getElementById('code').scrollIntoView({ behavior: 'smooth' }); 858 }); 859 860 document.querySelectorAll('nav a[href^="#"]').forEach(link => { 861 link.addEventListener('click', (e) => { 862 const target = document.querySelector(link.getAttribute('href')); 863 if (target) { 864 e.preventDefault(); 865 target.scrollIntoView({ behavior: 'smooth' }); 866 } 867 }); 868 }); 869 </script> 870 <!-- ===== FLOATING CHAT BUTTON ===== --> 871 <style> 872 .chat-float { 873 position: fixed; 874 bottom: 28px; 875 right: 28px; 876 z-index: 9999; 877 display: flex; 878 flex-direction: column; 879 align-items: flex-end; 880 gap: 10px; 881 } 882 883 /* Speech bubble */ 884 .chat-bubble { 885 background: #0c0c0c; 886 border: 1px solid #1e1e1e; 887 border-radius: 16px 16px 4px 16px; 888 padding: 12px 18px; 889 max-width: 200px; 890 color: #e5e5e5; 891 font-size: 0.85rem; 892 line-height: 1.5; 893 box-shadow: 0 8px 24px rgba(0,0,0,0.6); 894 opacity: 0; 895 transform: translateY(10px) scale(0.95); 896 animation: bubbleIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) 0.6s forwards; 897 font-family: 'Inter', system-ui, sans-serif; 898 } 899 .chat-bubble .greeting { 900 font-weight: 600; 901 color: #3b82f6; 902 } 903 .chat-bubble .sub { 904 color: #8a8a8a; 905 font-size: 0.75rem; 906 } 907 908 @keyframes bubbleIn { 909 to { 910 opacity: 1; 911 transform: translateY(0) scale(1); 912 } 913 } 914 915 /* Circular button */ 916 .chat-btn { 917 display: flex; 918 align-items: center; 919 justify-content: center; 920 width: 60px; 921 height: 60px; 922 border-radius: 50%; 923 background: #3b82f6; 924 border: none; 925 box-shadow: 0 4px 20px rgba(59, 130, 246, 0.4); 926 cursor: pointer; 927 transition: transform 0.2s, box-shadow 0.2s; 928 text-decoration: none; 929 color: white; 930 font-size: 28px; 931 } 932 .chat-btn:hover { 933 transform: scale(1.05); 934 box-shadow: 0 6px 28px rgba(59, 130, 246, 0.6); 935 } 936 .chat-btn:active { 937 transform: scale(0.95); 938 } 939 940 /* Hide bubble on small screens if needed */ 941 @media (max-width: 480px) { 942 .chat-bubble { 943 max-width: 160px; 944 font-size: 0.75rem; 945 padding: 10px 14px; 946 } 947 .chat-btn { 948 width: 52px; 949 height: 52px; 950 font-size: 24px; 951 } 952 } 953 </style> 954 955 <div class="chat-float"> 956 <!-- Speech bubble --> 957 <div class="chat-bubble"> 958 <div class="greeting">Hello,</div> 959 <div>How can I help you?</div> 960 <div class="sub">💬 Ask about Pytml</div> 961 </div> 962 963 <!-- Button linking to /bot --> 964 <a href="https://pytml.js.org/bot" class="chat-btn" aria-label="Open chat support"> 965 💬 966 </a> 967 </div> 968 </body> 969 </html>