Published on
32 min read

CRISPResso2: the standard tool for CRISPR NGS analysis (full tutorial)

Authors
  • avatar
    Name
    BioTech Bench
Table of contents

The first time I analyzed a CRISPR homology-directed repair (HDR) experiment with CRISPResso2, I opened the summary table, saw Unmodified%: 34.3 and Modified%: 65.7 on the reference row, and reported those numbers at lab meeting. Twenty minutes later, a postdoc asked a simple question: if 34.3% of my reads were unmodified and 65.7% had indels, where did the 17.8% HDR knock-in reads go? They add up to 117.8%.

I had fallen into the most common trap in CRISPResso2: reading the per-amplicon Unmodified% and Modified% columns as percentages of total sequencing reads, when they are actually percentages of reads aligned to that specific reference sequence. Once you supply an -e (--expected_hdr_amplicon_seq) sequence, CRISPResso2 splits your reads across two competing reference amplicons (Reference and HDR), and the percentage columns in CRISPResso_quantification_of_editing_frequency.txt stop meaning what you think they mean.

Published by Kendell Clement and colleagues in Luca Pinello's lab in 2019 (Clement et al., Nature Biotechnology 2019), CRISPResso2 is the gold-standard command-line tool for quantifying targeted amplicon next-generation sequencing (NGS) data from Cas9, Cas12a, base editing, and prime editing experiments. Unlike general-purpose read mappers (bwa-mem or bowtie2), CRISPResso2 uses a custom Needleman-Wunsch global alignment algorithm that penalizes gaps according to where they fall relative to the predicted Cas9 cleavage site, pulling split indels directly into the cut site so frameshift and allele frequencies are counted accurately.

In Post 12 on Sanger + TIDE/ICE, we saw how trace decomposition estimates indels for free when you are screening a handful of clones. In Post 13 on designing an amplicon NGS strategy, we covered how to design two-step barcoded PCR primers so your target site sits cleanly inside paired-end Illumina overlap. Now your demultiplexed .fastq.gz files are sitting on disk. This tutorial walks through running CRISPResso2 (v2.3.4) end-to-end on real paired-end amplicon datasets — covering SpCas9 NHEJ + ssODN HDR knock-in, adenine base editing (ABE8e), and high-throughput plate analysis with CRISPRessoBatch.

What you'll learn

  • How to install CRISPResso2 (v2.3.4) cleanly using Docker/Podman or Bioconda without getting stuck in Python 3.11 dependency conflicts, and what changed when fastp replaced FLASh and Trimmomatic in v2.3.0+
  • How CRISPResso2's quantification window (-w and -wc) works, why the default 2-bp window (-w 1 -wc -3) is built for SpCas9 blunt cuts, and how it silently discards base edits if you forget to widen it
  • How to run a complete SpCas9 cleavage + HDR knock-in + coding frameshift analysis using -a, -g, -e, and -c, and how to calculate the true wild-type, NHEJ, imperfect HDR, and clean HDR percentages from the raw output tables
  • How to analyze base editing (ABE8e and CBE) amplicon reads using --base_editor_output, how to separate clean target conversion from bystander edits in the allele table, and why nucleotide percentage tables alone overestimate therapeutic editing
  • How to process 24 to 384 samples in a single command with CRISPRessoBatch, including mixing different editing modalities and per-sample quantification windows inside one TSV file
  • Six real-world traps that skew CRISPResso2 numbers — from PCR primer dimers and pseudogene co-amplification to imperfect HDR indels landing outside the default window

How CRISPResso2 processes your FASTQ files under the hood

Before running a single command, it helps to understand the five steps CRISPResso2 executes on every sample. Almost every confusing number in a CRISPResso2 report traces back to a parameter in one of these five stages:

  1. Read quality filtering and paired-end merging (fastp): Starting in CRISPResso2 v2.3.0, the older FLASh and Trimmomatic binaries were replaced by fastp. When you pass paired-end files (-r1 and -r2), CRISPResso2 runs fastp --merge requiring at least a 10-bp overlap (--overlap_len_require 10) between Read 1 and the reverse-complement of Read 2. Merged full-length amplicon reads are written to out.extendedFrags.fastq.gz and passed to the aligner.
  2. Deduplication into unique sequence classes: Amplicon sequencing is highly redundant — in a 50,000-read sample, the wild-type allele alone might appear 20,000 times. Instead of running 50,000 separate pairwise alignments, CRISPResso2 collapses identical merged reads into unique sequences, aligns each unique sequence once (N_COMPUTED_ALN), and caches the result for all identical reads (N_CACHED_ALN). That is why a 5,000-read amplicon finishes aligning in less than a second.
  3. Cut-site-aware Needleman-Wunsch alignment: Standard NGS aligners use seed-and-extend heuristics that break when a read contains a 25-bp deletion or a 12-bp insertion, and they place gap openings arbitrarily inside microhomologies (GAAGAA). CRISPResso2 aligns the full merged read globally against your reference amplicon (-a), applying an alignment bonus at the predicted cleavage site (-wc, default -3 bp upstream of the PAM for SpCas9) so ambiguous indel gaps are anchored at the actual double-strand break.
  4. Competitive multi-reference assignment (for HDR or prime editing): When you provide an expected HDR amplicon (-e), CRISPResso2 aligns every read to both the wild-type Reference amplicon and the HDR amplicon. Each read is assigned to whichever amplicon gives the higher alignment score (provided it clears --min_identity_score, default 60).
  5. Quantification-window filtering: This is the step that surprises most users. CRISPResso2 does not count indels or substitutions across the entire amplicon. Why? Because PCR polymerases and Illumina sequencers introduce background substitution errors (0.1%–0.5% per base) across a 200-bp read. If CRISPResso2 counted every mismatch across 200 bp, a completely unedited wild-type control would look 15%–25% "modified." Instead, CRISPResso2 only counts mutations that overlap a narrow quantification window centered at spacer_end + quantification_window_center (-wc, default -3) and extending quantification_window_size (-w, default 1) bp on either side.
ParameterShort flagDefaultWhat it controlsRecommended setting by modality
--amplicon_seq-a(required)Full wild-type PCR amplicon sequence (5'→3', forward primer to reverse-complement of reverse primer)Exact full-length PCR product (150–280 bp)
--guide_seq-gNone20-nt protospacer sequence (5'→3', without the PAM)Always provide so cut site and allele windows are anchored
--quantification_window_center-wc-3Center of the quantification window relative to the 3' end of the guide-3 for SpCas9; +1 for Cas12a; -10 for ABE/CBE
--quantification_window_size-w1Base pairs extending on each side of -wc (1 = 2-bp window flanking cut)1 for SpCas9 NHEJ; 3–5 for Cas12a; 10 for base editors
--expected_hdr_amplicon_seq-eNoneFull amplicon sequence carrying your intended HDR knock-in editFull-length edited amplicon (same primers as -a)
--coding_seq-cNoneSubsequence of -a corresponding to coding exon(s), starting on codon boundaryExact in-frame exonic slice of -a (length divisible by 3)
--base_editor_output(none)FalseGenerates nucleotide conversion plots and substitution tablesPass flag for any CBE or ABE experiment
--write_cleaned_report(none)FalseRemoves your local filesystem path and command string from the HTML reportUseful when sharing HTML reports with collaborators

Step 1: Installing CRISPResso2 (v2.3.4) without dependency headaches

You have two ways to run CRISPResso2: the Pinello Lab's free web portal (crispresso2.pinellolab.org) for single samples under 100 MB, or the local command-line tool (CRISPResso, CRISPRessoBatch, CRISPRessoPooled, CRISPRessoWGS, CRISPRessoCompare). If you have more than three samples — or if your institution prohibits uploading human patient sequences to external servers — you want the command-line version.

A quick warning if you install via Conda: CRISPResso2 on Bioconda currently fails to solve on Python 3.11+ (LibMambaUnsatisfiableError caused by matplotlib-base, pillow, and libtiff pin conflicts). If you use Conda or Mamba, pin python=3.10 explicitly when creating the environment:

conda create -n crispresso2_env -c bioconda -c conda-forge python=3.10 crispresso2 -y

Even better, I strongly recommend running the official container image (docker.io/pinellolab/crispresso2:latest) with Docker or Podman. The container ships with CRISPResso 2.3.4, fastp 1.3.3, bowtie2, and samtools pre-compiled inside an isolated Pixi environment, so it runs identically on Linux, macOS, and Windows WSL2 and never breaks when you update your system Python.

Let's verify our container installation and check the version banner:

podman run --rm docker.io/pinellolab/crispresso2 CRISPResso --version
CRISPResso version 2.3.4
[Note that as of version 2.3.0 FLASh and Trimmomatic have been replaced by fastp for read merging and trimming. Accordingly, the --flash_command and --trimmomatic_command parameters have been replaced with --fastp_command. Also, --trimmomatic_options_string has been replaced with --fastp_options_string.

Also in version 2.3.2, when running CRISPRessoPooled in mixed-mode (amplicon file and genome are provided) the default behavior will be as if the --demultiplex_only_at_amplicons parameter is provided. This change means that reads and amplicons do not need to align to the exact locations.]

Docker vs Podman note: In every command below, I use podman run --rm -v $(pwd):/DATA:Z -w /DATA docker.io/pinellolab/crispresso2. If you use Docker on macOS or Ubuntu, simply replace podman with docker and drop the :Z SELinux volume suffix (-v $(pwd):/DATA). If you installed CRISPResso2 in a Conda environment, drop the container prefix and start directly with CRISPResso or CRISPRessoBatch.


Step 2: Preparing our EMX1 exon 3 amplicon sequences and FASTQ files

To make this tutorial 100% reproducible, we will analyze two paired-end Illumina amplicon datasets (5,000 read pairs each, 2×125 bp) targeting human EMX1 exon 3 — the classic benchmark locus from Ran et al. (Nature Protocols 2013):

  • Wild-type amplicon (WT_AMP, 180 bp): Amplified with forward primer CCATCCCCTTCTGTGAATGT and reverse primer CTAGTCATTGGAGGTGACAT.
  • Protospacer (SGRNA, 20 nt): GAGTCCGAGCAGAAGAAGAA (followed immediately by the GGG PAM at positions 94–96 of the amplicon; SpCas9 cuts between bases 90 and 91, three bases upstream of GGG).
  • Expected HDR amplicon (HDR_AMP, 186 bp): An ssODN knock-in donor that inserts a 6-bp GAATTC (EcoRI) restriction site right at the Cas9 cut site (...CAGAAGAAGAA|TTCGAAGGA...) and introduces a silent GGG→GGA PAM-blocking mutation so Cas9 cannot recut the repaired allele.
  • Coding sequence (CODING_SEQ, 120 bp / 40 codons): The in-frame coding slice of our wild-type amplicon starting at base 31 (GGA GCA GCT...), which lets CRISPResso2 classify every indel as in-frame (3n) or frameshift (3n ± 1).

Let's store those four sequences in a params.sh file and inspect the FASTQ files in our working directory:

cat << 'EOF' > params.sh
WT_AMP="CCATCCCCTTCTGTGAATGTTAGACCCATGGGAGCAGCTGGTCAGAGGGGACCCCGGCCTGGGGCCCCTAACCCTATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCATCACATCAACCGGTGGCGCATTGCCACGAAGCAGGCCAATGGGGAGGACATCGATGTCACCTCCAATGACTAG"
HDR_AMP="CCATCCCCTTCTGTGAATGTTAGACCCATGGGAGCAGCTGGTCAGAGGGGACCCCGGCCTGGGGCCCCTAACCCTATGGAGTCCGAGCAGAAGAAGAATTCGAAGGACTCCCATCACATCAACCGGTGGCGCATTGCCACGAAGCAGGCCAATGGGGAGGACATCGATGTCACCTCCAATGACTAG"
CODING_SEQ="GGAGCAGCTGGTCAGAGGGGACCCCGGCCTGGGGCCCCTAACCCTATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCATCACATCAACCGGTGGCGCATTGCCACGAAGCAGGCCAATGGG"
SGRNA="GAGTCCGAGCAGAAGAAGAA"
EOF

Let's check our input FASTQ files (emx1_cas9_hdr_R1.fastq.gz, emx1_cas9_hdr_R2.fastq.gz for the SpCas9 + ssODN HDR experiment, and emx1_abe8e_R1.fastq.gz, emx1_abe8e_R2.fastq.gz for the ABE8e adenine base editing experiment) and preview the first read record:

zcat emx1_cas9_hdr_R1.fastq.gz | head -n 4
@SIM_CAS9:1:1:1:1001/1
CCATCCCCTTCTGTGAATGTTAGACCCATGGGAGCAGCTGGTCAGAGGGGACCCCGGCCTGGGGCCCCTAACCCTATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCATCACATCAACCGGTGGCG
+
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII

Notice two crucial details about the sequences in params.sh before we run CRISPResso:

  1. -g (SGRNA) must NOT include the GGG PAM. One of the most frequent beginner mistakes is pasting the 23-nt sequence (GAGTCCGAGCAGAAGAAGAAGGG) into -g. Because CRISPResso2 measures the cut site (-wc -3) from the 3' end of -g, including the 3-nt PAM shifts the predicted cleavage site 3 bp downstream into the PAM itself, causing CRISPResso2 to miss small 1-bp deletions at the real cut site.
  2. -c (CODING_SEQ) must be an exact substring of -a (WT_AMP) and start on the first base of a codon. Never paste the full-length mRNA or cDNA sequence of your gene into -c if your PCR primers sit in introns or only amplify part of an exon. Take the exonic portion of -a, find the reading frame in your plasmid editor or genome browser, and trim the 5' and 3' ends so the sequence starts at codon position 1 and its length is a multiple of 3 (here, 120 bp = 40 codons).

Step 3: Running CRISPResso2 on a Cas9 NHEJ + HDR knock-in sample

Let's source params.sh and run CRISPResso on emx1_cas9_hdr_R1.fastq.gz and emx1_cas9_hdr_R2.fastq.gz, passing -a "$WT_AMP", -g "$SGRNA", -e "$HDR_AMP", and -c "$CODING_SEQ", and naming our output -n emx1_cas9_hdr. Afterward, we'll inspect the execution log saved in CRISPResso_on_emx1_cas9_hdr/CRISPResso_RUNNING_LOG.txt:

source params.sh && \
podman run --rm -v $(pwd):/DATA:Z -w /DATA docker.io/pinellolab/crispresso2 \
  CRISPResso \
  -r1 emx1_cas9_hdr_R1.fastq.gz \
  -r2 emx1_cas9_hdr_R2.fastq.gz \
  -a "$WT_AMP" \
  -g "$SGRNA" \
  -e "$HDR_AMP" \
  -c "$CODING_SEQ" \
  -n emx1_cas9_hdr

Let's read the execution log (CRISPResso_RUNNING_LOG.txt) to verify how fastp merged the paired-end reads and how many unique alleles were aligned:

cat CRISPResso_on_emx1_cas9_hdr/CRISPResso_RUNNING_LOG.txt
CRISPResso version 2.3.4
[Command used]:
/CRISPResso2/.pixi/envs/default/bin/CRISPResso -r1 emx1_cas9_hdr_R1.fastq.gz -r2 emx1_cas9_hdr_R2.fastq.gz -a CCATCCCCTTCTGTGAATGTTAGACCCATGGGAGCAGCTGGTCAGAGGGGACCCCGGCCTGGGGCCCCTAACCCTATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCATCACATCAACCGGTGGCGCATTGCCACGAAGCAGGCCAATGGGGAGGACATCGATGTCACCTCCAATGACTAG -g GAGTCCGAGCAGAAGAAGAA -e CCATCCCCTTCTGTGAATGTTAGACCCATGGGAGCAGCTGGTCAGAGGGGACCCCGGCCTGGGGCCCCTAACCCTATGGAGTCCGAGCAGAAGAAGAATTCGAAGGACTCCCATCACATCAACCGGTGGCGCATTGCCACGAAGCAGGCCAATGGGGAGGACATCGATGTCACCTCCAATGACTAG -c GGAGCAGCTGGTCAGAGGGGACCCCGGCCTGGGGCCCCTAACCCTATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCATCACATCAACCGGTGGCGCATTGCCACGAAGCAGGCCAATGGG -n emx1_cas9_hdr

[Execution log]:
CRISPRessoPro not installed
Computing quantification windows
Added 0 guides with flexible matching
	Original flexiguides: ['None']
	Found guides: []
	Mismatch locations: []
Added 0 guides with flexible matching
	Original flexiguides: ['None']
	Found guides: []
	Mismatch locations: []
Counting reads in input
Processing sequences with fastp...
Read1 before filtering:
total reads: 5000
total bases: 625000
Q20 bases: 625000(100%)
Q30 bases: 625000(100%)
Q40 bases: 625000(100%)

Read2 before filtering:
total reads: 5000
total bases: 625000
Q20 bases: 625000(100%)
Q30 bases: 625000(100%)
Q40 bases: 625000(100%)

Merged and filtered:
total reads: 5000
total bases: 901190
Q20 bases: 901190(100%)
Q30 bases: 901190(100%)
Q40 bases: 901190(100%)

Filtering result:
reads passed filter: 10000
reads failed due to low quality: 0
reads failed due to too many N: 0
reads corrected by overlap analysis: 0
bases corrected by overlap analysis: 0

Duplication rate: 99.82%

Insert size peak (evaluated by paired-end reads): 180

Read pairs merged: 5000
% of original read pairs: 100%
% in reads after filtering: 100%


JSON report: CRISPResso_on_emx1_cas9_hdr/fastp_report.json
HTML report: CRISPResso_on_emx1_cas9_hdr/fastp_report.html

fastp -i emx1_cas9_hdr_R1.fastq.gz -I emx1_cas9_hdr_R2.fastq.gz --merge --merged_out CRISPResso_on_emx1_cas9_hdr/out.extendedFrags.fastq.gz --out1 CRISPResso_on_emx1_cas9_hdr/out.notCombined_1.fastq.gz --out2 CRISPResso_on_emx1_cas9_hdr/out.notCombined_2.fastq.gz --overlap_len_require 10 --thread 1 --json CRISPResso_on_emx1_cas9_hdr/fastp_report.json --html CRISPResso_on_emx1_cas9_hdr/fastp_report.html --disable_adapter_trimming --disable_trim_poly_g --disable_quality_filtering --disable_length_filtering
fastp v1.3.3, time used: 2 seconds
Done!
Done!
Counting reads after preprocessing...
Aligning sequences...
Finished reading fastq file; 9 unique reads found of 5000 total reads found
Processing Reads; 0 Completed out of 9 Unique Reads
Finished reads; N_TOT_READS: 5000 N_COMPUTED_ALN: 9 N_CACHED_ALN: 4991 N_COMPUTED_NOTALN: 0 N_CACHED_NOTALN: 0
Done!
Quantifying indels/substitutions...
Done!
Calculating allele frequencies...
Done!
Saving processed data...
Making Plots...
Plotting read bar plot
Plotting read class pie chart and bar plot
Plotting alleles homology histogram
Processing plots for amplicon Reference...
Plotting nucleotide quilt across amplicon
Plotting nucleotide distribuition around sgRNA GAGTCCGAGCAGAAGAAGAA for Reference
Plotting indel size distribution for Reference
Plotting frequency deletions/insertions for Reference
Plotting amplication modifications for Reference
Plotting modification frequency for Reference
Plotting quantification window locations for Reference
Plotting position dependent indel for Reference
Plotting global modifications with respect to reference
Plotting HDR nucleotide quilt
Plotting frameshift analysis for Reference
Plotting frameshift frequency for Reference
Plotting non-coding mutation positions for Reference
Plotting potential splice sites
Plotting allele distribution around cut for Reference
Plotting amino acids for Reference
Done!
Processing plots for amplicon HDR...
Plotting nucleotide quilt across amplicon
Plotting nucleotide distribuition around sgRNA GAGTCCGAGCAGAAGAAGAA for HDR
Plotting indel size distribution for HDR
Plotting frequency deletions/insertions for HDR
Plotting amplication modifications for HDR
Plotting modification frequency for HDR
Plotting quantification window locations for HDR
Plotting position dependent indel for HDR
Plotting global modifications with respect to reference
Plotting frameshift analysis for HDR
Plotting frameshift frequency for HDR
Plotting non-coding mutation positions for HDR
Plotting potential splice sites
Plotting allele distribution around cut for HDR
Plotting amino acids for HDR
Done!
Plotting global frameshift in-frame mutations pie chart
Plotting global frameshift in-frame mutation profiles
Plotting global potential splice sites pie chart
Done!
Removing Intermediate files...
Low number of total reads: <10000. Total reads: 5000.
Disproportionate percentages of reads were aligned to amplicon: Reference, Percent of aligned reads aligned to this amplicon: 82.2%.
Disproportionate percentages of reads were aligned to amplicon: HDR, Percent of aligned reads aligned to this amplicon: 17.8%.

                                        _
                                       '  )
                                       .-'
                                      (____
                                   C)|     \
                                     \     /
                                      \___/

Analysis Complete!

Look at the diagnostic numbers in CRISPResso_RUNNING_LOG.txt:

  • Insert size peak (evaluated by paired-end reads): 180 and Read pairs merged: 5000 (100%). Every single read pair merged cleanly. If your Read pairs merged drops below 70%, either your amplicon is longer than (R1_len + R2_len - 10) or the 3' ends of your reads have degraded quality scores.
  • 9 unique reads found of 5000 total reads found: CRISPResso2 computed 9 Needleman-Wunsch alignments (N_COMPUTED_ALN: 9) and reused cached alignments for the remaining 4,991 reads (N_CACHED_ALN: 4991).
  • At the bottom of the log, CRISPResso2 prints two warnings (Disproportionate percentages of reads were aligned to amplicon: Reference... 82.2% and HDR... 17.8%). Don't panic when you see that warning — CRISPResso2 assumes multi-amplicon runs might be pooled multiplexed targets that should be equimolar, whereas in an HDR experiment it is completely normal for Reference (WT + NHEJ) to get 80%+ of the reads and HDR to get 15%–20%.

Decoding CRISPResso_quantification_of_editing_frequency.txt (and avoiding the denominator trap)

Now let's print the core output file that everyone imports into R or Python, CRISPResso_quantification_of_editing_frequency.txt:

cat CRISPResso_on_emx1_cas9_hdr/CRISPResso_quantification_of_editing_frequency.txt
Amplicon	Unmodified%	Modified%	Reads_in_input	Reads_aligned_all_amplicons	Reads_aligned	Unmodified	Modified	Discarded	Insertions	Deletions	Substitutions	Only Insertions	Only Deletions	Only Substitutions	Insertions and Deletions	Insertions and Substitutions	Deletions and Substitutions	Insertions Deletions and Substitutions
Reference	34.30656934	65.69343066	5000	5000	4110	1410	2700	0	640	2060	0	640	2060	0	0	0	0	0
HDR	100.0	0.0	5000	5000	890	890	0	0	0	0	0	0	0	0	0	0	0	0

Here is the exact trap I mentioned in the introduction. Look at the Reference row:

  • Reads_aligned_all_amplicons: 5000
  • Reads_aligned (to Reference): 4110
  • Unmodified: 1410
  • Modified: 2700 (640 insertions + 2060 deletions)
  • Unmodified%: 34.30656934 (1410 / 4110, not 1410 / 5000!)
  • Modified%: 65.69343066 (2700 / 4110, not 2700 / 5000!)

If you report Modified% = 65.7% as your NHEJ rate and 890 / 5000 = 17.8% as your HDR rate, your denominators don't match. Whenever you run CRISPResso2 with an -e HDR sequence, always divide the raw read counts (Unmodified and Modified) by Reads_aligned_all_amplicons (5000):

  • Unmodified wild-type reads: 1,410 / 5,000 = 28.20%
  • NHEJ indel reads (aligned to Reference, Modified): 2,700 / 5,000 = 54.00% (12.80% insertions + 41.20% deletions)
  • Reads aligned to HDR amplicon: 890 / 5,000 = 17.80%
CRISPResso2 alignment and editing frequency barplot for SpCas9 plus HDR knock-in at EMX1

Figure 1. Overall read alignment and editing classification barplot (1c.Alignment_barplot.png) generated by CRISPResso 2.3.4 for our _EMX1 SpCas9 + ssODN HDR sample. Of 5,000 merged reads, 4,110 (82.2%) aligned best to the wild-type Reference amplicon (1,410 unmodified wild-type and 2,700 modified NHEJ reads) and 890 (17.8%) aligned best to the HDR amplicon. Output of the code above._

Wait — are all 890 HDR reads really 100% clean HDR?

Look back at the second row of CRISPResso_quantification_of_editing_frequency.txt: HDR says Unmodified%: 100.0, Unmodified: 890, Modified: 0. Does that mean all 890 reads carry a flawless GAATTC knock-in with zero secondary indels?

Let's check the allele frequency tables (HDR.Alleles_frequency_table_around_sgRNA_GAGTCCGAGCAGAAGAAGAA.txt and Reference.Alleles_frequency_table_around_sgRNA_GAGTCCGAGCAGAAGAAGAA.txt) to see the actual sequences of those reads:

cat CRISPResso_on_emx1_cas9_hdr/HDR.Alleles_frequency_table_around_sgRNA_GAGTCCGAGCAGAAGAAGAA.txt \
    CRISPResso_on_emx1_cas9_hdr/Reference.Alleles_frequency_table_around_sgRNA_GAGTCCGAGCAGAAGAAGAA.txt
Aligned_Sequence	Reference_Sequence	Unedited	n_deleted	n_inserted	n_mutated	#Reads	%Reads
ATGGAGTCCGAGCAGAAGAAGAATTCGAAGGACTCCCATC	ATGGAGTCCGAGCAGAAGAAGAATTCGAAGGACTCCCATC	True	0	0	0	745	14.899999999999999
ATGGAGTCCGAGCAGAAGAAGAATTC-AAGGACTCCCATC	ATGGAGTCCGAGCAGAAGAAGAATTCGAAGGACTCCCATC	True	0	0	0	145	2.9000000000000004
Aligned_Sequence	Reference_Sequence	Unedited	n_deleted	n_inserted	n_mutated	#Reads	%Reads
ATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCATCACATCA	ATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCATCACATCA	True	0	0	0	1410	28.199999999999996
ATGGAGTCCGAGCAGAAGA-GAAGGGCTCCCATCACATCA	ATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCATCACATCA	False	1	0	0	985	19.7
ATGGAGTCCGAGCAGAAGAAAGAAGGGCTCCCATCACATC	ATGGAGTCCGAGCAGAAGAA-GAAGGGCTCCCATCACATC	False	0	1	0	640	12.8
ATGGAGTCCGAGCAGAAG--GAAGGGCTCCCATCACATCA	ATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCATCACATCA	False	2	0	0	415	8.3
ATGGAGTCCGAGCAGAAGAA---GGGCTCCCATCACATCA	ATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCATCACATCA	False	3	0	0	310	6.2
ATGGAGTCCGAGCAGAAGA----GGGCTCCCATCACATCA	ATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCATCACATCA	False	4	0	0	225	4.5
ATGGAGTCCGAGCAG--------GGGCTCCCATCACATCA	ATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCATCACATCA	False	8	0	0	125	2.5

Look closely at the second line of the HDR allele table: ATGGAGTCCGAGCAGAAGAAGAATTC-AAGGACTCCCATC has 145 reads (2.90% of all reads) with a 1-bp deletion (- where G should be right after GAATTC), yet Unedited is marked True and n_deleted is 0!

Why did CRISPResso2 score a read with a 1-bp deletion as Unedited = True on the HDR amplicon? Because on the HDR amplicon, CRISPResso2 maps the original 20-nt guide GAGTCCGAGCAGAAGAAGAA (bases 77–96) and places the default -w 1 -wc -3 quantification window between bases 93 and 94. Meanwhile, our 6-bp GAATTC insertion pushed the downstream junction 6 bp to the right (to base 103)! That 1-bp synthesis or end-joining deletion at base 103 fell outside the 2-bp quantification window on the HDR reference, so CRISPResso2 ignored it.

This happens constantly with ssODN knock-ins (where synthesis errors in ultramer donors or microhomology-mediated end joining at the 3' junction create 1-bp indels 5–10 bp away from the original cut site). If you want CRISPResso2 to catch indels across the entire knock-in junction automatically, widen -w to 10 (or inspect HDR.Alleles_frequency_table_around_sgRNA_*.txt directly, where %Reads already gives you the exact breakdown: 14.90% clean HDR and 2.90% imperfect HDR).

CRISPResso2 allele frequency visualization around the EMX1 SpCas9 cut site

Figure 2. Allele-level alignment plot (9.Reference.Alleles_frequency_table_around_sgRNA_GAGTCCGAGCAGAAGAAGAA.png) for reads aligned to the wild-type Reference amplicon. Note that %Reads in the right-hand column is normalized to all 5,000 aligned reads across both amplicons (28.20% wild-type, 19.70% -1 bp deletion, 12.80% +1 bp A-insertion, 8.30% -2 bp deletion, 6.20% -3 bp in-frame deletion, 4.50% -4 bp deletion, and 2.50% -8 bp deletion). Output of the code above.

Checking coding frameshifts (Reference.Frameshift_analysis.txt)

Because we supplied -c "$CODING_SEQ", CRISPResso2 also translates every NHEJ allele in Reference and checks whether the net indel length is divisible by 3 or shifts the reading frame. Let's inspect Reference.Frameshift_analysis.txt:

cat CRISPResso_on_emx1_cas9_hdr/Reference.Frameshift_analysis.txt
Frameshift analysis:
	Noncoding mutation:0 reads
	In-frame mutation:310 reads
	Frameshift mutation:2390 reads

Out of 2,700 NHEJ-modified reads, 2,390 reads (88.5% of NHEJ reads, or 47.8% of total reads) carry frameshift mutations (+1, -1, -2, -4, and -8 bp), while 310 reads (11.5% of NHEJ reads, or 6.2% of total reads) carry the -3 bp (AAG) deletion that removes a single lysine/glutamate codon boundary without shifting the downstream reading frame. If you were doing a functional knockout rather than an HDR knock-in, that 6.2% in-frame deletion population is the exact reason you must check protein loss by Western blot or sequence single-cell clones rather than assuming "54% indels = 54% knockout."


Step 4: Analyzing base editing (ABE8e and CBE) without losing your edits

Now let's look at our second experiment: editing the same EMX1 guide (GAGTCCGAGCAGAAGAAGAA) with the hyperactive adenine base editor ABE8e, which converts adenines (A) to guanines (G, read as T→C on the opposite strand) inside positions 3 to 9 of the protospacer (counting PAM as positions 21–23).

In our EMX1 protospacer (G1 A2 G3 T4 C5 C6 G7 A8 G9 C10 A11 G12 A13 A14 G15 A16 A17 G18 A19 A20), our target adenine is A8 (which sits right in the middle of the canonical ABE8e activity window). However, there is a nearby bystander adenine at A11 (just on the 3' edge of the window) and another at A2 (on the 5' edge).

What happens if you forget -w 10 -wc -10 on base editing data?

Remember that CRISPResso2's default quantification window is -w 1 -wc -3, which only looks at positions 17 and 18 of the protospacer (spacer_end - 3 ± 1). In a base editing experiment, your deaminase edits occur at positions 3 to 11 of the protospacer — 6 to 15 bp upstream of the Cas9 nickase site!

If you run CRISPResso2 with default -w 1 -wc -3 on emx1_abe8e_R1.fastq.gz, CRISPResso2 completely ignores every A→G substitution at A2, A8, and A11, reports 98.7% Unmodified (counting only the 1.3% nickase -1 bp deletions at position 17), and prints this warning at the end of the run:

WARNING: >=0.2% of substitutions were outside of the quantification window. Total substitutions: 4505, Substitutions outside window: 4505.

To center the quantification window across the entire 20-nt protospacer, we set:

  • -wc -10 (centers the window 10 bp upstream of the 3' end of the 20-nt guide, right between positions 10 and 11 of the protospacer)
  • -w 10 (extends 10 bp to the left and 10 bp to the right, covering all 20 bases of the protospacer)
  • --base_editor_output --conversion_nuc_from A --conversion_nuc_to G (turns on base-conversion heatmaps and nucleotide frequency tables; for cytosine base editors like BE4max or evoCDA1, use --conversion_nuc_from C --conversion_nuc_to T or G to A if your guide is on the reverse strand)

Let's run CRISPResso with those base-editor parameters on emx1_abe8e_R1.fastq.gz and emx1_abe8e_R2.fastq.gz:

source params.sh && \
podman run --rm -v $(pwd):/DATA:Z -w /DATA docker.io/pinellolab/crispresso2 \
  CRISPResso \
  -r1 emx1_abe8e_R1.fastq.gz \
  -r2 emx1_abe8e_R2.fastq.gz \
  -a "$WT_AMP" \
  -g "$SGRNA" \
  -w 10 \
  -wc -10 \
  --base_editor_output \
  --conversion_nuc_from A \
  --conversion_nuc_to G \
  -n emx1_abe8e

Now let's inspect the editing frequency table and the quantification-window nucleotide percentage table:

cat CRISPResso_on_emx1_abe8e/CRISPResso_quantification_of_editing_frequency.txt
Amplicon	Unmodified%	Modified%	Reads_in_input	Reads_aligned_all_amplicons	Reads_aligned	Unmodified	Modified	Discarded	Insertions	Deletions	Substitutions	Only Insertions	Only Deletions	Only Substitutions	Insertions and Deletions	Insertions and Substitutions	Deletions and Substitutions	Insertions Deletions and Substitutions
Reference	28.4	71.6	5000	5000	5000	1420	3580	0	0	65	3515	0	65	3515	0	0	0	0

With -w 10 -wc -10, CRISPResso2 captures all 3,515 substitution reads (70.3%) and 65 nickase deletion reads (1.3%), leaving 1,420 unmodified wild-type reads (28.4%).

Per-position conversion vs. clean allele frequency (the bystander problem)

When --base_editor_output is enabled, CRISPResso2 writes Quantification_window_nucleotide_percentage_table.txt, which shows the exact fraction (0.0 to 1.0) of A, C, G, T, N, and - (deletion) at each of the 20 positions of the protospacer:

cat CRISPResso_on_emx1_abe8e/Quantification_window_nucleotide_percentage_table.txt
	G	A	G	T	C	C	G	A	G	C	A	G	A	A	G	A	A	G	A	A
A	0.0	0.971	0.0	0.0	0.0	0.0	0.0	0.306	0.0	0.0	0.822	0.0	1.0	1.0	0.0	1.0	0.987	0.0	1.0	1.0
C	0.0	0.0	0.0	0.0	1.0	1.0	0.0	0.0	0.0	1.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0
G	1.0	0.029	1.0	0.0	0.0	0.0	1.0	0.694	1.0	0.0	0.178	1.0	0.0	0.0	1.0	0.0	0.0	1.0	0.0	0.0
T	0.0	0.0	0.0	1.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0
N	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0
-	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.0	0.013	0.0	0.0	0.0

Read down the G row across the 20 protospacer columns:

  • At A8 (column 8), G is 0.694 (69.4% A→G conversion).
  • At A11 (column 11), G is 0.178 (17.8% bystander A→G conversion).
  • At A2 (column 2), G is 0.029 (2.9% bystander A→G conversion).
  • At A17 (column 17, right at the nCas9(D10A) nick site), - is 0.013 (1.3% deletion indel byproduct).

Here is why Sanger trace tools like EditR or MultiEditR cannot replace amplicon NGS + CRISPResso2: from the per-position table alone (which is all Sanger traces can see), you know that A8 is edited in 69.4% of reads and A11 is edited in 17.8% of reads, but you have no idea how many reads carry only the desired A8→G edit without a bystander mutation.

In CRISPResso2, every read is a single DNA molecule, so Alleles_frequency_table_around_sgRNA_GAGTCCGAGCAGAAGAAGAA.txt gives us the exact haplotype phase:

cat CRISPResso_on_emx1_abe8e/Alleles_frequency_table_around_sgRNA_GAGTCCGAGCAGAAGAAGAA.txt
Aligned_Sequence	Reference_Sequence	Unedited	n_deleted	n_inserted	n_mutated	#Reads	%Reads
TAACCCTATGGAGTCCGGGCAGAAGAAGAAGGGCTCCCAT	TAACCCTATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCAT	False	0	0	1	2480	49.6
TAACCCTATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCAT	TAACCCTATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCAT	True	0	0	0	1420	28.4
TAACCCTATGGAGTCCGGGCGGAAGAAGAAGGGCTCCCAT	TAACCCTATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCAT	False	0	0	2	845	16.900000000000002
TAACCCTATGGGGTCCGGGCAGAAGAAGAAGGGCTCCCAT	TAACCCTATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCAT	False	0	0	2	145	2.9000000000000004
TAACCCTATGGAGTCCGAGCAGAAGA-GAAGGGCTCCCAT	TAACCCTATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCAT	False	1	0	0	65	1.3
TAACCCTATGGAGTCCGAGCGGAAGAAGAAGGGCTCCCAT	TAACCCTATGGAGTCCGAGCAGAAGAAGAAGGGCTCCCAT	False	0	0	1	45	0.8999999999999999

Look at what the allele table reveals:

  • Clean A8→G target edit only (...GAGTCCGGGCAGAAGAA...): 2,480 reads (49.60%)
  • Unedited wild-type (...GAGTCCGAGCAGAAGAA...): 1,420 reads (28.40%)
  • Double edit A8→G + A11→G (...GAGTCCGGGCGGAAGAA...): 845 reads (16.90%)
  • Double edit A2→G + A8→G (...GGGTCCGGGCAGAAGAA...): 145 reads (2.90%)
  • Nickase -1 bp deletion (...GAGTCCGAGCAGAAGA-GAA...): 65 reads (1.30%)
  • Bystander A11→G only (...GAGTCCGAGCGGAAGAA...): 45 reads (0.90%)

Wait: notice how 49.60% + 16.90% + 2.90% = 69.40% (the total A8→G conversion rate from the nucleotide table), and 16.90% + 0.90% = 17.80% (the total A11→G conversion rate)! While total A8→G conversion is 69.4%, clean single-base editing (A8→G with zero bystanders) is 49.6%. If A11→G causes a missense mutation in your protein, 49.6% is the true functional editing efficiency — and if you want to push that higher, you would swap the hyperactive ABE8e deaminase for a narrower-window variant like ABE8e(TadA-8e V106W) or ABE9 (as we covered in Post 6 on base editing guide design).

CRISPResso2 allele frequency plot for ABE8e base editing at EMX1

Figure 3. Allele distribution table (9.Alleles_frequency_table_around_sgRNA_GAGTCCGAGCAGAAGAAGAA.png) from our ABE8e run on _EMX1 (-w 10 -wc -10 --base_editor_output). Substitution bases are highlighted in bold red boxes, immediately separating the clean A8→G allele (49.60%) from A8+A11 (16.90%) and A2+A8 (2.90%) bystander alleles. Output of the code above._


Step 5: Processing a full plate of samples with CRISPRessoBatch

Nobody sequences a single amplicon on an Illumina run. In practice, you have 24, 96, or 384 demultiplexed FASTQ pairs from a screening plate — including biological replicates, non-targeting controls, and sometimes a mix of Cas9 HDR and base-edited wells.

Instead of writing a bash for loop and manually stitching together 96 separate CRISPResso_quantification_of_editing_frequency.txt files, use CRISPRessoBatch. You pass a tab-separated settings file (--batch_settings batch_samples.tsv) where each row is a sample and each column header is any valid CRISPResso long parameter name (name, fastq_r1, fastq_r2, expected_hdr_amplicon_seq, quantification_window_size, quantification_window_center, base_editor_output, and so on). Any parameter that is identical across all samples (like -a "$WT_AMP" and -g "$SGRNA") can be passed once on the command line.

Even better, because per-row columns override command-line defaults, you can analyze our Cas9 HDR sample (quantification_window_size = 1, quantification_window_center = -3) and our ABE8e sample (quantification_window_size = 10, quantification_window_center = -10, base_editor_output = True) in the same batch run!

Let's create batch_samples.tsv and run CRISPRessoBatch:

cat << 'EOF' > batch_samples.tsv
name	fastq_r1	fastq_r2	expected_hdr_amplicon_seq	quantification_window_size	quantification_window_center	base_editor_output
emx1_cas9_hdr	emx1_cas9_hdr_R1.fastq.gz	emx1_cas9_hdr_R2.fastq.gz	CCATCCCCTTCTGTGAATGTTAGACCCATGGGAGCAGCTGGTCAGAGGGGACCCCGGCCTGGGGCCCCTAACCCTATGGAGTCCGAGCAGAAGAAGAATTCGAAGGACTCCCATCACATCAACCGGTGGCGCATTGCCACGAAGCAGGCCAATGGGGAGGACATCGATGTCACCTCCAATGACTAG	1	-3	False
emx1_abe8e	emx1_abe8e_R1.fastq.gz	emx1_abe8e_R2.fastq.gz		10	-10	True
EOF

Now let's run CRISPRessoBatch (adding --skip_failed so that in a 96-well plate, one empty PCR well doesn't abort the entire batch, and -p 4 on multi-core machines if you want parallel execution) and print the combined batch summary table:

source params.sh && \
podman run --rm -v $(pwd):/DATA:Z -w /DATA docker.io/pinellolab/crispresso2 \
  CRISPRessoBatch \
  --batch_settings batch_samples.tsv \
  -a "$WT_AMP" \
  -g "$SGRNA" \
  -n emx1_batch_run \
  --skip_failed && \
cat CRISPRessoBatch_on_emx1_batch_run/CRISPRessoBatch_quantification_of_editing_frequency.txt
Batch	Amplicon	Unmodified%	Modified%	Reads_in_input	Reads_aligned_all_amplicons	Reads_aligned	Unmodified	Modified	Discarded	Insertions	Deletions	Substitutions	Only Insertions	Only Deletions	Only Substitutions	Insertions and Deletions	Insertions and Substitutions	Deletions and Substitutions	Insertions Deletions and Substitutions
emx1_cas9_hdr	Reference	34.30656934	65.69343066	5000	5000	4110	1410	2700	0	640	2060	0	640	2060	0	0	0	0	0
emx1_cas9_hdr	HDR	100.0	0.0	5000	5000	890	890	0	0	0	0	0	0	0	0	0	0	0	0
emx1_abe8e	Reference	28.4	71.6	5000	5000	5000	1420	3580	0	0	65	3515	0	65	3515	0	0	0	0

Look how clean CRISPRessoBatch_quantification_of_editing_frequency.txt is: a single tab-separated file with a Batch column on the left, ready to load straight into R (readr::read_tsv()) or Python (pandas.read_csv(sep="\t")) to plot your entire 96-well experiment in seconds.


Bonus: Prime editing (--prime_editing_pegRNA_spacer_seq) and CRISPRessoCompare

Two additional modes in CRISPResso2 are worth knowing when your projects move beyond Cas9 NHEJ/HDR and base editing:

  1. Prime editing mode (--prime_editing_pegRNA_spacer_seq): Prime editing (covered in Post 4 and Post 7) uses a nickase Cas9-RT fusion and a pegRNA containing a spacer, a primer-binding site (PBS), and a reverse-transcriptase template (RTT) — plus an optional nicking sgRNA (PE3). Instead of constructing the full edited amplicon by hand for -e, you can pass --prime_editing_pegRNA_spacer_seq, --prime_editing_pegRNA_extension_seq, and (for PE3) --prime_editing_nicking_guide_seq. CRISPResso2 automatically reconstructs the expected prime-edited reference amplicon and generates a third competing reference (Prime-edited_scaffold_incorporation) that quantifies how often the pegRNA scaffold read-through gets reverse-transcribed into the genomic locus — one of the most common prime-editing byproducts.
  2. Comparing treated vs. negative-control samples (CRISPRessoCompare): If your amplicon spans a homopolymer tract (such as AAAAAAA or GGGGGG) where Illumina sequencing polymerase slippage produces 2%–4% false deletions in your unedited control, run CRISPResso on both your edited sample (CRISPResso_on_treated) and your mock-transfected control (CRISPResso_on_mock), then run:
    CRISPRessoCompare CRISPResso_on_treated CRISPResso_on_mock -n treated_vs_mock
    
    CRISPRessoCompare subtracts position-specific background indel and substitution rates observed in the mock sample from the treated sample. (When you are looking for ultra-rare off-target cleavage below 0.1% across dozens of candidate loci, we will use a dedicated statistical caller, CRISPECTOR, in the next post in this series.)

Where CRISPResso2 breaks down: 6 traps that skew your numbers

Even though CRISPResso2 is the standard tool in the field, it can only count the DNA molecules that successfully amplify and align. Here are six failure modes that routinely fool experienced labs:

1. Large deletions (>100 bp) and chromosomal translocations vanish before sequencing

As we discussed in Post 13 on amplicon NGS design, SpCas9 double-strand breaks produce deletions of 100 bp to several kilobases in 5%–20% of alleles (Kosicki et al., Nature Biotechnology 2018). Whenever a deletion removes one of your PCR primer binding sites, that allele fails to amplify during PCR1 ("allele dropout"). In a diploid clone where one allele has a -1 bp deletion and the other allele has a -450 bp deletion that deletes your forward primer site, CRISPResso2 will report 99.8% -1 bp deletion and you will mistakenly call a hemizygous clone "homozygous." Always check copy number or run a long-range PCR across single-cell clones if zygosity matters.

2. Primer dimers and non-specific bands inflate Discarded reads unless you check --min_identity_score

By default, CRISPResso2 discards any merged read whose alignment identity score to -a falls below --min_identity_score 60 (60%). However, if your PCR produces primer dimers (40–60 bp) or short artifacts, check CRISPResso_mapping_statistics.txt and 1b.Alignment_pie_chart.png. If more than 10% of Reads_in_input land in Discarded, you either have primer dimers (fix with a 0.8x–1.0x AMPure XP bead cleanup before sequencing) or you pasted the wrong reference orientation into -a.

3. Variants near the 5' or 3' ends of the amplicon are distorted by primer binding

Never place your Cas9 guide within 25 bp of your forward or reverse PCR1 primer. During PCR amplification, the synthetic oligonucleotide primer overwrites whatever genomic sequence it anneals to, masking deletions that extend into the primer footprint and preventing mutated templates from annealing at all. Keep at least 50 bp of flanking genomic sequence between the 3' end of each primer and your predicted cut site.

4. Pseudogenes and paralogs masquerade as "substitutions" or "wild-type" reads

If your target gene has a close paralog or processed pseudogene (for example, HBB vs. HBD, or PMS2 vs. PMS2CL) and your primers amplify both loci, the paralog reads will align to your reference amplicon. If the paralog is identical inside the 2-bp quantification window (-w 1), it artificially inflates Unmodified% and makes a 90% knockout look like 45% editing. Always run Primer-BLAST on your PCR1 primers before ordering them.

5. Donor template contamination in HDR experiments

If you transfect cells with high concentrations of plasmid donor or ssODN and harvest genomic DNA 24–48 hours later, residual episomal donor DNA in the lysate can act as a megaprimer during PCR ("PCR jumping" or template switching), creating chimeric amplicons that look like genuine chromosomal HDR reads in CRISPResso2. To prevent this, always design both of your PCR1 primers in genomic DNA outside the 5' and 3' homology arms of your donor, and wait at least 72 hours (or passage the cells once) before extracting gDNA.

6. Default --plot_window_size 20 hides wide deletions in the allele table figure

Notice that CRISPResso2 has two separate window parameters:

  • -w (--quantification_window_size, default 1): determines which reads are counted as modified.
  • -pw (--plot_window_size, default 20): determines how many bases around the cut site are displayed in the allele table figure (9.Alleles_frequency_table_around_sgRNA_*.png, 20 bp on each side = 40 bp total).

If your experiment has frequent 25–40 bp deletions (common with Cas12a or dual-nickase strategies), -w 1 will still count them as long as they cross the cut site, but -pw 20 will collapse different long deletions that look identical inside the middle 40 bp into the same visual row. Increase -pw 35 or -pw 40 when visualizing wider indels.


How I run CRISPResso2 in practice

After running hundreds of amplicon plates through CRISPResso2, here is the exact checklist I follow on every project:

  1. Always run the official Docker/Podman container (docker.io/pinellolab/crispresso2) and record the exact version string (CRISPResso version 2.3.4) from CRISPResso_RUNNING_LOG.txt in your lab notebook or methods section.
  2. Always include an unedited negative control sample in every CRISPRessoBatch plate. If your mock-transfected control shows more than 0.5% indels or more than 1% substitutions inside the quantification window, you have a sequencing quality issue, a homopolymer artifact, or sample cross-contamination during PCR setup.
  3. Never trust the summary percentage columns blindly when -e or --base_editor_output is active.
    • For HDR (-e), compute rates against Reads_aligned_all_amplicons and inspect HDR.Alleles_frequency_table_around_sgRNA_*.txt (or widen -w) to separate clean HDR from imperfect HDR (HDR + indel).
    • For base editing (--base_editor_output), always pass -w 10 -wc -10 and report both the per-position conversion rate at your target nucleotide and the percentage of clean target-edited alleles from Alleles_frequency_table_around_sgRNA_*.txt.
  4. Always supply -c (--coding_seq) for protein-coding knockouts. Reviewers increasingly ask for frameshift vs. in-frame percentages rather than total indel percentage, and passing an in-frame coding substring to -c gives you Reference.Frameshift_analysis.txt automatically.

What is the strangest allele or unexpected artifact CRISPResso2 has uncovered in your sequencing data? Drop a comment below — I'd love to hear how your runs turned out.

Resources

Tool / PaperWhat it isLink
Clement et al. (2019)Original Nature Biotechnology paper introducing CRISPResso2 (37(3):224–226)doi:10.1038/s41587-019-0032-3
Pinello et al. (2016)Original CRISPResso paper in Nature Biotechnology (34(7):695–697)doi:10.1038/nbt.3583
CRISPResso2 GitHubOfficial source code, documentation, CLI parameter reference, and issue trackergithub.com/pinellolab/CRISPResso2
CRISPResso2 Web PortalFree browser interface for single-sample analysis (Pinello Lab)crispresso2.pinellolab.org
CRISPResso2 Docker ImageOfficial pre-built container (docker.io/pinellolab/crispresso2)hub.docker.com/r/pinellolab/crispresso2
fastp (Chen et al., 2018)Ultra-fast FASTQ preprocessor and paired-end merger used by CRISPResso2 v2.3.0+doi:10.1093/bioinformatics/bty560
Kosicki et al. (2018)Nature Biotechnology paper showing frequent large deletions at Cas9 cut sitesdoi:10.1038/nbt.4192
Ran et al. (2013)Nature Protocols CRISPR-Cas9 genome engineering protocol (EMX1 benchmark guide)doi:10.1038/nprot.2013.143
CRISPR from Bench to AnalysisThe complete book covering CRISPR wet-lab protocols, NGS pipelines, and troubleshootingko-fi.com/s/5cd0378971