{"spec_id":"line-loss-training","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// line-loss-training: Training Loss Curve\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-09-05\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Fixed-seed LCG so runs are reproducible without a browser RNG.\nlet seed = 42;\nfunction lcgRandom() {\n  seed = (seed * 1664525 + 1013904223) % 4294967296;\n  return seed / 4294967296;\n}\n\nconst epochs = 60;\nconst epochArray = [];\nconst trainLoss = [];\nconst valLoss = [];\n\nfor (let epoch = 1; epoch <= epochs; epoch++) {\n  epochArray.push(epoch);\n  const trainNoise = (lcgRandom() - 0.5) * 0.04;\n  const valNoise = (lcgRandom() - 0.5) * 0.09;\n  const trainValue = 0.15 + 2.1 * Math.exp(-epoch / 12) + trainNoise;\n  // Validation loss tracks training early on, then overfits and creeps back up.\n  const overfitTerm = epoch > 22 ? 0.0026 * Math.pow(epoch - 22, 1.35) : 0;\n  const valValue = 0.28 + 2.3 * Math.exp(-epoch / 13) + overfitTerm + valNoise;\n  trainLoss.push(Math.max(0.05, Number(trainValue.toFixed(4))));\n  valLoss.push(Math.max(0.08, Number(valValue.toFixed(4))));\n}\n\nlet minValEpoch = 1;\nlet minValLoss = valLoss[0];\nfor (let i = 0; i < valLoss.length; i++) {\n  if (valLoss[i] < minValLoss) {\n    minValLoss = valLoss[i];\n    minValEpoch = epochArray[i];\n  }\n}\n\n// --- Chart -------------------------------------------------------------------\nHighcharts.chart(\"container\", {\n  chart: {\n    type: \"line\",\n    backgroundColor: \"transparent\",\n    animation: false,\n    style: { fontFamily: \"inherit\" },\n  },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"line-loss-training · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  xAxis: {\n    title: { text: \"Epoch\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    min: 1,\n    max: epochs,\n    plotLines: [\n      {\n        value: minValEpoch,\n        color: t.amber,\n        width: 1.5,\n        dashStyle: \"Dash\",\n        zIndex: 3,\n        label: {\n          text: `Best epoch: ${minValEpoch}`,\n          style: { color: t.inkSoft, fontSize: \"13px\" },\n          y: 16,\n        },\n      },\n    ],\n  },\n  yAxis: {\n    title: { text: \"Cross-Entropy Loss\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    min: 0,\n  },\n  legend: {\n    itemStyle: { color: t.inkSoft, fontSize: \"14px\" },\n    itemHoverStyle: { color: t.ink },\n  },\n  plotOptions: {\n    series: {\n      animation: false,\n      marker: { enabled: false },\n      lineWidth: 2.75,\n    },\n  },\n  tooltip: { enabled: false },\n  series: [\n    {\n      name: \"Training loss\",\n      data: trainLoss,\n      color: t.palette[0],\n    },\n    {\n      name: \"Validation loss\",\n      data: valLoss,\n      color: t.palette[1],\n    },\n    {\n      name: \"Best epoch (min val. loss)\",\n      type: \"scatter\",\n      data: [[minValEpoch, minValLoss]],\n      color: t.amber,\n      marker: { enabled: true, radius: 7, symbol: \"circle\", lineWidth: 1.5, lineColor: t.ink },\n      enableMouseTracking: false,\n    },\n  ],\n});\n"}