Quality Control of NGS Reads with FastQC and MultiQC
Chapter 3 of 5: Adapters and trimming
Why adapters appear at the end of reads
During library preparation, short adapter sequences are joined to both ends of every DNA fragment (the insert). The sequencer reads from the adapter into the insert. If the insert is longer than the read length, the read contains only insert. If the insert is shorter, the read runs through the whole insert and into the adapter on the other side.

How fastp finds adapters in paired-end data: read 1 and read 2 come from the same fragment, so when the insert is short they overlap. fastp aligns read 1 with the reverse complement of read 2. Where they overlap, it knows exactly where the insert ends, and any bases beyond that point are adapter. This works even if you do not know the adapter sequence. For single-end data, fastp looks at the first reads, finds k-mers that appear unusually often near read ends and assembles them into the likely adapter. With --detect_adapter_for_pe, it also runs this detection on paired-end data as a second check.
▸ Go deeper: the maths of adapter read-through, adapter sequences and dimers
A read contains adapter whenever the insert is shorter than the read length, and the adapter starts exactly at position insert length + 1. So FastQC’s adapter-content curve is really the cumulative distribution of your insert sizes: if 23% of fragments are shorter than 150 bp, about 23% of 150 bp reads will contain adapter by their last base. That is exactly what you will see in the tutorial dataset.
| Library type | Adapter start (read 1) | FastQC name |
|---|---|---|
| Illumina TruSeq and most ligation kits | AGATCGGAAGAGC… | Illumina Universal Adapter |
| Nextera / tagmentation (incl. ATAC-seq) | CTGTCTCTTATACACATCT | Nextera Transposase Sequence |
| Illumina small RNA 3′ | TGGAATTCTCGG… | Illumina Small RNA 3′ Adapter |
Adapter dimers form when two adapters join with no insert between them. Such reads start with adapter at position 1, appear as an overrepresented sequence beginning with the adapter, and become zero-length after trimming. A few percent is common; a large fraction means too little input DNA or poor size selection.
🔬 Researcher notes: how the main tools actually find adapters
- fastp, paired-end: overlap analysis. Read 1 is aligned to the reverse complement of read 2; where they overlap, the insert end is known exactly, so no adapter sequence is needed. In the overlap, fastp also corrects a mismatched base when one read has high quality and the other very low (Chen et al. 2018).
- fastp, single-end: adapter detection by assembling high-frequency read tails: frequent k-mers near read ends are used as seeds and extended into the full adapter. Adding
--detect_adapter_for_peto paired data typically finds about 0.1–0.5% more adapters, because overlap detection can fail when errors are present (fastp documentation). - Cutadapt: semiglobal (ends-free) alignment of the given adapter against each read, accepting matches up to a maximum error rate counted as mismatches plus indels (Martin 2011; Cutadapt documentation). It is precise but needs the adapter sequence.
- Trimmomatic: “simple” mode aligns known adapters with seed-and-extend; “palindrome” mode detects read-through in pairs from the reverse-complement relationship of the two reads, which finds even very short adapter fragments (Bolger et al. 2014).
- If your library has UMIs, extract them into the read name first (for example
fastp --umi) so trimming does not destroy them.
Step 4: Trim adapters and low-quality bases with fastp
fastp -i sample_R1.fastq.gz -I sample_R2.fastq.gz \
-o trim_R1.fastq.gz -O trim_R2.fastq.gz \
--detect_adapter_for_pe \
--cut_tail --cut_window_size 4 --cut_mean_quality 20 \
--length_required 36 \
-h fastp.html -j fastp.json -w 4What each option does:
-i,-I: input read 1 and read 2;-o,-O: output files (pairs stay in sync)--detect_adapter_for_pe: adds sequence-based adapter detection on top of the overlap method--cut_tail --cut_window_size 4 --cut_mean_quality 20: sliding-window trimming from the 3′ end (see the algorithm below)--length_required 36: drops reads shorter than 36 bp after trimming, because very short reads map to many places-h,-j: fastp’s own HTML and JSON reports; MultiQC reads the JSON-w 4: 4 worker threads
fastp also applies some filters by default: it removes reads with too many low-quality bases or too many N bases, and on NextSeq and NovaSeq data it trims poly-G tails (these two-colour instruments report “no signal” as G).
▸ Go deeper: fastp defaults you are using without knowing it, and the three cutting modes
- Quality filter (on by default): a base below Q15 counts as “unqualified” (
-q 15); a read is discarded if more than 40% of its bases are unqualified (-u 40) or it has more than 5 N bases (-n 5). - Length filter: default minimum 15 bp (
-l 15); this tutorial raises it to 36. - Poly-G trimming: switched on automatically for NovaSeq and NextSeq data (
-gforces it,-Gdisables it). - Pairs stay in sync: if one mate fails, both are dropped unless you ask fastp to keep unpaired reads (
--unpaired1,--unpaired2).
| fastp mode | What it does | Use it when |
|---|---|---|
--cut_tail | Window moves from the 3′ end towards the start; low-quality windows are dropped until a good one is found | Standard quality trimming (this tutorial) |
--cut_front | The same, from the 5′ end towards the 3′ end | Poor first cycles |
--cut_right | Window moves from the 5′ end; at the first bad window, that window and everything to its right are removed | Strict, Trimmomatic SLIDINGWINDOW-like; can cut reads short |
| Task | fastp | Trimmomatic | Cutadapt |
|---|---|---|---|
| Adapters (paired) | automatic, or --adapter_sequence | ILLUMINACLIP:adapters.fa:2:30:10 | -a ADAPTER -A ADAPTER |
| 3′ quality trimming | --cut_tail --cut_mean_quality 20 | SLIDINGWINDOW:4:20 | -q 20 |
| Minimum length | -l 36 | MINLEN:36 | -m 36 |
The sliding-window trimming algorithm
Instead of cutting at the first bad base (one random low score would cut too much), fastp looks at the average quality of a small window of bases:

read = list of quality scores, length n
w = window size (4)
T = mean quality threshold (20)
end = n
while end >= w:
window = read[end - w : end]
if mean(window) < T:
end = end - w # drop the bases in this window
else:
break # good window found: stop
keep read[0 : end]
if end < length_required: discard the read (and its mate)Because it averages, one low base inside good sequence is kept, while a run of bad bases at the end is removed. The same idea is used by Trimmomatic’s SLIDINGWINDOW option.
▸ Go deeper: the partial-sum algorithm used by Cutadapt and BWA
Cutadapt’s -q option uses the same algorithm as BWA: subtract the cutoff from every quality, add the results up from the 3′ end, and cut where that running sum is lowest. Worked example from the Cutadapt documentation, cutoff 10:
| Step | Values (5′ → 3′) |
|---|---|
| Qualities | 42, 40, 26, 27, 8, 7, 11, 4, 2, 3 |
| Minus cutoff 10 | 32, 30, 16, 17, −2, −3, 1, −6, −8, −7 |
| Partial sums from the 3′ end | (70), (38), 8, −8, −25, −23, −20, −21, −15, −7 |
| Result | The minimum, −25, sits at base 5, so the read keeps bases 1–4 (42, 40, 26, 27) |
Notice the Q11 base: it is above the cutoff, but it sits inside a bad tail, so it is removed too. A simple “cut at the first base below 10” rule would have kept it. Trimmomatic’s MAXINFO mode goes further and balances read length against expected errors; its authors recommend it over SLIDINGWINDOW.
🔬 Researcher notes: does trimming actually help? What the evidence says
Trimming is not automatically good. Two widely cited studies:
- Williams et al. 2016 (BMC Bioinformatics): with the most aggressive quality trimming, over 10% of genes changed their estimated expression significantly. Most of the effect came from very short reads mapping spuriously, and a minimum-length filter removed most of it. No or modest trimming gave the most biologically accurate estimates.
- Liao & Shi 2020 (NAR Genomics and Bioinformatics): aligners that soft-clip handle adapters and poor read ends themselves. Gene-level quantification from untrimmed reads was comparable to, or slightly better than, trimmed reads when checked against RT-qPCR, and total analysis time fell by up to an order of magnitude.
- MacManes 2014 (Frontiers in Genetics): for de novo transcriptome assembly, gentle quality trimming (about Phred 2–5) performed better than aggressive trimming.
| Application | Recommended practice |
|---|---|
| Bulk RNA-seq gene counts | Adapter trimming only (or none with a soft-clipping aligner); always use a minimum length filter |
| WGS / WES variant calling | Adapter trimming plus light 3′ quality trimming (Q15–20); keep pairs; recalibrate base qualities later |
| De novo assembly | Adapter trimming plus gentle quality trimming |
| Small RNA, amplicon | Adapter (and primer) trimming is essential |
| Low-quality or degraded samples | Trimming matters most here; check how many reads you lose |