{"spec_id":"ma-differential-expression","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// ma-differential-expression: MA Plot for Differential Expression\n// Library: highcharts 12.6.0 | JavaScript 22.22.3\n// Quality: 86/100 | Created: 2026-06-21\n\nconst t = window.ANYPLOT_TOKENS;\n\n// Muted color for non-significant genes (theme-adaptive)\nconst MUTED = window.ANYPLOT_THEME === \"light\" ? \"#6B6A63\" : \"#A8A79F\";\n\n// Deterministic LCG — no seeded Math.random in browser\nlet seed = 42;\nfunction lcg() {\n    seed = (seed * 1664525 + 1013904223) >>> 0;\n    return seed / 0x100000000;\n}\nfunction normal() {\n    let u;\n    do { u = lcg(); } while (u === 0);\n    return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * lcg());\n}\n\n// Generate RNA-seq MA plot data (~12 000 genes, typical whole-transcriptome)\nconst N = 12000;\nconst genes = [];\nfor (let i = 0; i < N; i++) {\n    // Mean expression A: skewed so more genes land at moderate values\n    const A = +(1 + Math.pow(lcg(), 0.55) * 13).toFixed(3);\n    // Variance (fan shape): higher at low expression, tighter at high\n    const sigma = Math.max(0.18, 1.7 / Math.sqrt(A + 0.5));\n    // Slight systematic upward bias at low expression (realistic normalisation artefact)\n    const bias = A < 4 ? 0.12 * (4 - A) / 4 : 0;\n    const M = +(normal() * sigma + bias).toFixed(3);\n    // Significance: genes with large |M| and sufficient coverage\n    const absM = Math.abs(M);\n    const sigScore = A > 2 ? (absM - 0.95) * 0.28 : 0;\n    const significant = sigScore > 0 && lcg() < sigScore;\n    genes.push({ A, M, significant });\n}\n\n// Partition into three series\nconst nonSig  = genes.filter(g => !g.significant).map(g => [g.A, g.M]);\nconst upReg   = genes.filter(g =>  g.significant && g.M > 0).map(g => [g.A, g.M]);\nconst downReg = genes.filter(g =>  g.significant && g.M < 0).map(g => [g.A, g.M]);\n\n// LOESS-like smoothed trend: sliding window average over sorted genes\nconst sorted = [...genes].sort((a, b) => a.A - b.A);\nconst WIN = 700;\nconst STEP = 70;\nconst loess = [];\nfor (let i = 0; i + WIN <= sorted.length; i += STEP) {\n    const chunk = sorted.slice(i, i + WIN);\n    const avgA = +(chunk.reduce((s, g) => s + g.A, 0) / WIN).toFixed(3);\n    const avgM = +(chunk.reduce((s, g) => s + g.M, 0) / WIN).toFixed(3);\n    loess.push([avgA, avgM]);\n}\n\nHighcharts.chart(\"container\", {\n    chart: {\n        type: \"scatter\",\n        backgroundColor: \"transparent\",\n        animation: false,\n        style: { fontFamily: \"inherit\" },\n        marginRight: 30,\n    },\n    credits: { enabled: false },\n    colors: t.palette,\n    title: {\n        text: \"ma-differential-expression · javascript · highcharts · anyplot.ai\",\n        style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n    },\n    xAxis: {\n        title: {\n            text: \"A — Mean Average Expression (log₂)\",\n            style: { color: t.inkSoft, fontSize: \"16px\" },\n        },\n        lineColor: t.inkSoft,\n        tickColor: t.inkSoft,\n        gridLineColor: t.grid,\n        gridLineWidth: 1,\n        labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    },\n    yAxis: {\n        title: {\n            text: \"M — Log₂ Fold Change\",\n            style: { color: t.inkSoft, fontSize: \"16px\" },\n        },\n        lineColor: t.inkSoft,\n        tickColor: t.inkSoft,\n        gridLineColor: t.grid,\n        gridLineWidth: 1,\n        labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n        plotLines: [\n            {\n                value: 0,\n                color: t.inkSoft,\n                width: 1.5,\n                dashStyle: \"Solid\",\n                zIndex: 4,\n            },\n            {\n                value: 1,\n                color: t.inkSoft,\n                width: 1,\n                dashStyle: \"ShortDash\",\n                zIndex: 4,\n                label: {\n                    text: \"+1 (2× up)\",\n                    align: \"right\",\n                    style: { color: t.inkSoft, fontSize: \"12px\" },\n                },\n            },\n            {\n                value: -1,\n                color: t.inkSoft,\n                width: 1,\n                dashStyle: \"ShortDash\",\n                zIndex: 4,\n                label: {\n                    text: \"−1 (2× down)\",\n                    align: \"right\",\n                    style: { color: t.inkSoft, fontSize: \"12px\" },\n                },\n            },\n        ],\n    },\n    legend: {\n        enabled: true,\n        align: \"right\",\n        verticalAlign: \"top\",\n        itemStyle: { color: t.inkSoft, fontSize: \"15px\" },\n        itemHoverStyle: { color: t.ink },\n        backgroundColor: t.elevatedBg,\n        borderColor: t.grid,\n        borderWidth: 1,\n        borderRadius: 4,\n        padding: 12,\n    },\n    tooltip: { enabled: false },\n    plotOptions: {\n        series: { animation: false },\n    },\n    series: [\n        {\n            name: \"Not significant\",\n            type: \"scatter\",\n            data: nonSig,\n            color: MUTED,\n            opacity: 0.40,\n            marker: { radius: 2, symbol: \"circle\" },\n            enableMouseTracking: false,\n        },\n        {\n            // Triangle-up + brand green: color AND shape encode upregulation (CVD-safe)\n            name: \"Upregulated (padj < 0.05)\",\n            type: \"scatter\",\n            data: upReg,\n            color: t.palette[0],\n            opacity: 0.85,\n            marker: { radius: 4, symbol: \"triangle\" },\n            enableMouseTracking: false,\n        },\n        {\n            // Triangle-down + matte red: color AND shape encode downregulation (CVD-safe)\n            name: \"Downregulated (padj < 0.05)\",\n            type: \"scatter\",\n            data: downReg,\n            color: t.palette[4],\n            opacity: 0.85,\n            marker: { radius: 4, symbol: \"triangle-down\" },\n            enableMouseTracking: false,\n        },\n        {\n            name: \"LOESS bias trend\",\n            type: \"spline\",\n            data: loess,\n            color: t.amber,\n            lineWidth: 2.5,\n            marker: { enabled: false },\n            enableMouseTracking: false,\n        },\n    ],\n});\n"}