What this site shows
Each layer on the map displays a single variable for a single year, across all 50 U.S. states. The full underlying dataset is a balanced state-year panel covering 1979–2024 for the law and crime variables, with shorter coverage windows for variables whose source series start later (NICS background checks from 1999; ACS demographics from 2008; RAND household ownership 1980–2016; firearm suicide / homicide / FS ownership proxy 1979–2023). Year sliders gracefully gray out states without data in that year.
The site is generated from CSV/JSON files in the same git repository. The data pipeline is in
scripts/build_website_data.py, which reads the project’s balanced panels and emits
docs/data/panel.json and docs/data/metadata.json. To rebuild after a data
update: python scripts/build_website_data.py.
Important caveats
- Background checks are not gun sales. NICS counts include some states’ permit re-checks, and a single check can cover multiple firearms. Treat NICS as a market-activity flow proxy, not a stock measure of gun owners.
- The FS/S ownership proxy is mechanically correlated with suicide outcomes. It is the share of all suicides committed with a firearm, so it should never be used as an explanatory variable when the outcome is suicide. The RAND HFR estimate (1980–2016) is the more defensible ownership measure for cross-state work.
- FBI rape definition changed in 2013. Pre- and post-2013 rape rates are not directly comparable.
- Tufts law coding stops at 2024. Bruen-era injunctions are not generally re-coded; check the Tufts narrative report for state-specific exceptions.
- 2026 data are incomplete. The map currently goes through 2024 because the latest full-year FBI crime release covers 2024.
Manipulations applied to the raw data
| Variable family | What was done | Why |
|---|---|---|
| Granular crime (homicide, robbery, ...) | The OpenCrime feed double-listed North Carolina 2022 and omitted North Dakota 2022. The second NC 2022 row (population ≈ 779k) was reassigned to ND. | Logged in data/processed/crime_repairs_log.csv. Applied identically in the website
build to keep all granular crime variables balanced. |
| Pre-ACS demographic shares (1990–2007) | Reconstructed annual non-Hispanic race shares, male share, and age-group shares from Census ASRH (1990–1999), the 2000s intercensal all-data file, and PEP (2010–2024). | ACS 1-year tables are only available from 2008. The reconstruction matches Census published denominators within rounding. |
| Bachelor’s+ share before 2008 | Linearly interpolated within state from Census Statistical Abstract Table 229 anchors (1990, 2000, 2008) and ACS 2008–2024 actuals. | Single-year educational attainment estimates are not consistently available before ACS. |
| SAIPE income / poverty | Missing 1990–1992 and 1994 values are linearly interpolated within state. | SAIPE skipped those years; interpolation preserves a balanced panel. |
| Per-capita income, household income | BEA per-capita personal income and SAIPE median household income are deflated to 2024 dollars using CPI-U. | Real-terms comparability across the long panel. |
| Firearm suicide rates / total suicide rates | Computed on the fly as firearm_suicides / total_population × 100000 (and
similarly for total suicides) from the firearm-suicide-share dataset. |
Source file ships counts and the FS/S ratio but not population-normalized rates. |
| Law-category indices | Derived as simple sums of the relevant Tufts binary indicators (background checks, carry restrictions, banned weapons, buyer restrictions, DV prohibitors, storage / GVRO). | Group-level totals are easier to read on a map than 72 individual indicators. The
category groupings are listed in scripts/build_website_data.py. |
All variables — State view (1979–2024)
Variables are grouped by category. Click a source link to open the original data provider in a new tab.
All variables — County view (2009–2024)
The county view exposes a different (smaller) set of variables: county crime from Jacob Kaplan's UCR aggregations, county demographics from ACS 5-year, county economy (SAIPE income/poverty + ERS unemployment + BEA per-capita income), and state firearm laws plus state firearm mortality joined down to every county in a state-year.
Data sources cited on this site
- Tufts CTSI — State Firearm Laws Database (1976–2024) · coded by Michael Siegel et al., 72 binary indicators across 11 categories.
- FBI Uniform Crime Reports / Crime Data Explorer · via OpenCrime extraction (state annual counts and rates).
- FBI NICS background checks · via Data Liberation Project.
- RAND TL-354 — State-Level Estimates of Household Firearm Ownership (1980–2016) · Schell, Peterson, Vegetabile, Scherling, Smart, Morral.
- Firearm suicide / homicide v2 dataset · long-run state firearm suicides, total suicides, firearm homicides, and the FS/S ownership proxy (1949–2023).
- U.S. Census Bureau Population Estimates Program · ASRH, intercensal all-data, PEP files.
- Census SAIPE · small-area income and poverty estimates.
- U.S. Census Bureau ACS 1-year detailed tables (2008–2024).
- FRED (Federal Reserve Bank of St. Louis) · BLS LAUS unemployment, BEA per-capita personal income (state).
- Census Statistical Abstract Table 229 · pre-ACS educational attainment anchors.
- Jacob Kaplan's Concatenated Files: Offenses Known and Clearances by Arrest, 1960–2024 · openICPSR project 100707, V22. Source for county-level UCR crime counts.
- USDA Economic Research Service county-level data sets · mirrors BLS LAUS county unemployment.
- BEA CAINC1 county personal income tables · per-capita personal income at the county grain.
How to reproduce
- Clone this repository.
- Install Python with
pandas,numpy, andopenpyxl. - State panels:
python scripts/build_firearms_panel.py(rebuilds the originaldata/processed/*.csvfrom raw inputs), thenpython scripts/audit_panels.pyandpython scripts/augment_panels.py. - County panel (Phase 1+2a):
python scripts/build_county_panel.py. Auto-fetches ACS 5-year via the Census API (cached) and reads PEP/SAIPE/ERS/BEA flat files fromdata/county/. - County crime (Phase 3): manually download Jacob Kaplan's openICPSR project 100707 V22 and unzip into
data/county/kaplan_offenses/, then runpython scripts/build_county_crime.py. Re-runbuild_county_panel.pyto merge the crime layer in. - Website data files:
python scripts/build_website_data.py(state) andpython scripts/build_website_county_data.py(county per-year files). - Preview:
python -m http.server 8000 -d docsthen openhttp://localhost:8000.
The full prose log of every data source and manipulation lives in data_appendix.md at the repository root.
Generated at —