Chatomics Field GuideWhat They Don't Teach You

Glossary · Visualization

Volcano plot

The one figure that decides which genes make it into your paper, so get the axis and the threshold right before you trust the shape.

By Ming "Tommy" Tang, Director of Bioinformatics in Big Pharma · Reviewed September 2026 · 2 min read

Definition

A volcano plot is a scatter plot used to visualize the output of a differential expression test, with one point per gene, transcript, protein, or metabolite. The x-axis is log2 fold change between two conditions, and the y-axis is -log10 of the p-value, usually the multiple-testing-adjusted p-value (FDR). Because variability tends to increase with fold-change magnitude, statistically confident hits cluster in the upper-left and upper-right corners of the plot, the two "wings" that give it its name. The plot itself performs no statistics; it is a visualization of a test that has already been run elsewhere, typically DESeq2, edgeR, or limma.

You meet the volcano plot the moment DESeq2, edgeR, or limma finishes and hands you a results table with a log fold change column and a p-value column. Nobody reads 20,000 rows of a table. The volcano plot compresses that table into one figure, and it becomes the figure that goes in the slide deck, the paper, and the grant renewal.

That compression is also the risk. Which axis you plot, which p-value column you use, and where you draw the cutoff lines all change which genes look like "hits." The plot doesn't test anything itself, it just shows you the trade-off between effect size and statistical confidence that your DE tool already computed, so a wrong choice here quietly rewrites your biological conclusion.

Why it matters

The axis choice alone can change your gene list. If you plot -log10 of the raw p-value instead of the adjusted p-value, you inflate the number of apparent hits, because testing 20,000 genes at a nominal p < 0.05 cutoff produces roughly 1,000 false positives by chance before any correction is applied. A volcano plot built on raw p-values looks dramatically more "significant" than one built on FDR, and if that gene list feeds into pathway enrichment, you're enriching on noise.

The threshold lines matter just as much. EnhancedVolcano's package default of |log2FC| > 2 and p < 1e-6 is far stricter than the common RNA-seq convention of |log2FC| > 1 with FDR < 0.05. Neither is "correct" in the abstract; the right choice depends on your sample size and how many downstream hits you can realistically validate. Report the exact cutoff you used in the figure legend or methods, because the same dataset plotted with different thresholds tells visibly different stories.

Where people get it wrong

The most common mistake is trusting an extreme point on the far x-axis wings without checking its underlying counts. A gene that jumps from 2 reads to 10 reads across replicates produces a 5-fold change with almost no real statistical power behind it, and it can still land far out on the plot because low counts inflate variance estimates. Always pull the raw or normalized counts for any gene you plan to highlight before you trust its position. A second, related mix-up is confusing the volcano plot with the MA plot: an MA plot puts log fold change on the y-axis against mean expression on the x-axis, and it's the plot you actually use to spot count-dependent bias, not the volcano plot.

A concrete example

A minimal ggplot2 volcano plot built directly from a DESeq2 results table, with threshold lines for a 2-fold change and FDR 0.05, following the standard log2FC-on-x, -log10(padj)-on-y mapping.

r
library(ggplot2)

res_df %>%
  mutate(sig = case_when(
    padj < 0.05 & log2FoldChange > 1  ~ "up",
    padj < 0.05 & log2FoldChange < -1 ~ "down",
    TRUE ~ "ns"
  )) %>%
  ggplot(aes(x = log2FoldChange, y = -log10(padj), color = sig)) +
  geom_point(alpha = 0.6) +
  geom_hline(yintercept = -log10(0.05), linetype = "dashed") +
  geom_vline(xintercept = c(-1, 1), linetype = "dashed") +
  scale_color_manual(values = c(up = "red", down = "blue", ns = "grey70"))

Related terms

Questions people ask

What is the difference between a volcano plot and an MA plot?

A volcano plot puts log2 fold change on the x-axis and -log10(p-value) on the y-axis, so it shows effect size against statistical confidence. An MA plot puts log2 fold change on the y-axis and mean expression (log scale) on the x-axis, which is the plot you check for count-dependent bias, like fold changes that shrink or inflate at low read counts. They answer different questions and neither replaces the other.

Should a volcano plot use raw p-values or adjusted p-values?

Use the adjusted (FDR) p-value, not the raw one. With tens of thousands of genes tested, a raw p < 0.05 cutoff produces hundreds to thousands of false positives by chance alone; the adjusted p-value corrects for that. If a plot or paper doesn't say which one is on the y-axis, assume the worst and check the code.

What fold change and p-value cutoffs should I use for a volcano plot?

There's no universal answer, but common defaults are |log2FC| > 1 (a 2-fold change) with adjusted p < 0.05, and EnhancedVolcano's package default is the stricter |log2FC| > 2 with p < 1e-6. Pick a threshold that matches your sample size and downstream use, and report it explicitly rather than hiding it in default plotting parameters.

How do I make a volcano plot in R?

The most common route is ggplot2 with geom_point(), mapping log2FC to x and -log10(adj p-value) to y, then adding geom_hline() and geom_vline() for the threshold lines and geom_label_repel() from ggrepel for gene labels. Bioconductor's EnhancedVolcano and the newer CRAN package ggvolcano (0.1.4, Feb 2025) wrap this into a single publication-ready function if you don't want to build it from scratch.

Why does a gene with a huge fold change sometimes not look significant on a volcano plot?

Low-count genes produce noisy, exaggerated fold-change estimates because a jump from 2 reads to 10 reads is a 5-fold change with almost no statistical power behind it. That's why DESeq2's default workflow applies fold-change shrinkage (lfcShrink) before plotting, and why you should check a gene's raw counts before trusting an extreme point out on the wings.

Related pages

Related reading on the blog

Sources

  1. EnhancedVolcano: publication-ready volcano plots with enhanced colouring and labeling — Default thresholds (|log2FC|>2, p<1e-6) and multi-attribute plotting
  2. VolcaNoseR is a web app for creating, exploring, labeling and sharing volcano plots — No-code volcano plot tool and typical adjusted p-value thresholds
  3. Creating volcano plots with ggplot2 - Tutorial — ggplot2 code pattern for axes, threshold lines, and gene labels
  4. ggvolcano: Publication-Ready Volcano Plots — CRAN package version and release date
  5. A global approach to analysis and interpretation of metabolic data for plant natural product discovery — Origin of the volcano shape and use across omics types