An interactive visualization of Luigi Boccherini’s complete string quartet output (1761-1804), displayed as a periodic table-inspired grid.
View it (via gisthack, see below for details):
# 1. Configure git to use GitHub CLI credentials (do this once)
gh auth setup-git
# 2. Save your gist ID (do this once)
gh gist list | head -1 | awk '{print $1}' > .gist_id
# 3. Create a sync script (do this once)
cat > sync-gist.sh << 'EOF'
#!/bin/bash
# Usage: ./sync-gist.sh "commit message" file1 file2 ...
# Or: ./sync-gist.sh file1 file2 ... (uses default message)
# If first arg looks like a commit message (has spaces or quotes), use it
if [[ "$1" == *" "* ]] || [[ "$1" == \"*\" ]]; then
MESSAGE="$1"
shift
FILES="$@"
else
MESSAGE="Update gist"
FILES="${@:-index.html}"
fi
# Add, commit, and push with message
git add $FILES
git commit -m "$MESSAGE"
git push
EOF
chmod +x sync-gist.sh
# 1. Make your changes to index.html, opera.json, or README.md
# 2. Check what changed
git diff # See all changes
git diff index.html # See specific file changes
# 3. Update the gist with a commit message
./sync-gist.sh "Fix title alignment" index.html
# Or update multiple files
./sync-gist.sh "Update visualization and docs" index.html README.md
# Or use default message "Update gist"
./sync-gist.sh index.html
The script uses standard git commands to update the gist:
git add - stages your filesgit commit -m "message" - commits with your messagegit push - pushes to the gistYour commit messages appear in the gist history instead of just “revised”.
# Standard git workflow
git add index.html
git commit -m "Your commit message"
git push
# Check status
git status
git log --oneline
Get the raw file URL and use githack.com to serve it:
Get the raw URL from your gist:
https://gist.githubusercontent.com/USERNAME/GIST_ID/raw/index.htmlReplace gist.githubusercontent.com with gist.githack.com:
https://gist.githack.com/USERNAME/GIST_ID/raw/index.htmlExample for this gist:
https://gist.githack.com/jsundram/a187ff2619b90eaf68d449462b1f9795/raw/index.htmlNote: htmlpreview.github.io doesn’t work because it can’t load external scripts like D3.js.
The data is organized as an array of opus groups, where each opus contains:
{
"opus": 2, // Opus number
"year": 1761, // Year of composition
"dedication": "...", // Optional: dedicatee
"imslp": "...", // Optional: IMSLP link for the opus
"quartets": [...] // Array of quartets in this opus
}
Each quartet contains:
{
"number": 1, // Quartet number within opus
"gerard": 159, // Gerard catalog number
"key": "C", // Key (e.g., "C", "E-flat")
"major": false, // true = major, false = minor
"nickname": "...", // Optional: nickname
"imslp": "...", // Optional: IMSLP link
"mvmts": [...], // Array of movement names
"category": "opera grande" // "opera grande" or "opera piccola" variants
}
Displayed in visualization:
Used in interactions:
Notable transformations:
All data fields are currently utilized either in the display, tooltips, or interactions. The data is comprehensive and fully integrated.
Single-file design: All HTML, CSS, and JavaScript in one file for easy gist hosting and sharing.
Technology stack:
1. Periodic Table Metaphor
2. Visual Alignment Strategy The design emphasizes vertical alignment across three levels:
Row Header ↔ Quartet Cards
─────────────────────────────────────
Year (age) ↔ Mode bar (G# / quartet #)
Opus number ↔ Key signature
Category badge ↔ Movement countThis creates strong visual relationships between semantically related information.
3. Color Encoding System
Mode bar (top of each card):
Movement count (diverging purple-green palette):
Category badges use the same palette:
Row background gradients:
4. Layout System
Flexbox throughout:
display: flex with flex-wrap for cardsjustify-content: space-between for vertical distributionflex-direction: column for stacking elementsHeight constraints:
flex-grow: 1 on middle sections (opus number, key signature) for vertical centeringmargin-top: auto to anchor to bottom5. Typography Hierarchy
Dramatic size contrast in row headers:
1. Commented sections for easy tweaking
The opus label CSS and JavaScript are heavily commented with clear section markers (e.g., === OPUS LABEL SECTION ===) to facilitate experimentation with layout and sizing.
2. Dynamic class application Category background gradients are applied dynamically:
const categoryClass = opus.quartets[0].category.includes('grande')
? 'grande-bg' : 'piccola-bg';
3. Unicode transformation Flat symbols are rendered using Unicode replacement:
const keyDisplay = quartet.key.replace('-flat', '♭');
4. Nested container pattern Cards use multiple nested containers for precise alignment:
quartet-card → mode-bar + card-contentcard-content → key-section + nickname + movements-countThis allows independent control of each vertical section.
5. Age calculation Boccherini’s age is calculated inline from a constant:
const BOCCHERINI_BIRTH_YEAR = 1743;
const age = opus.year - BOCCHERINI_BIRTH_YEAR;
Requires modern browser support for:
Tested in Chrome, Firefox, Safari, and Edge (2023+).
Created: December 2025 Data source: Luigi Boccherini quartet catalog (G.159-249)