Image compression for writers and developers
A modern, privacy-focused image compression tool built with Nuxt 4. Squish helps writers and developers optimize their images with a beautiful, interactive preview comparison slider and real-time quality adjustments.
- π Privacy First: All processing happens locally in your browser β your images never leave your device
- ποΈ Live Preview Comparison: Interactive side-by-side slider to compare original vs compressed images
- π Zoom & Pan: Magnify up to 800% with mouse wheel zoom and click-and-drag panning for pixel-level inspection
- β‘ Real-time Updates: See compression effects instantly as you adjust quality settings
- π Large Preview Window: Optimized for wide screens with up to 2000px layout for detailed inspection
- π¨ Modern UI: Dark theme with clean, intuitive interface using Nuxt UI components
- π± Responsive Design: Works seamlessly across desktop, tablet, and mobile devices
- π― Smart Quality Control: Color-coded warnings (green/yellow/red) for quality levels with inline compression stats
- π¦ Batch Processing: Compress multiple images at once
- πΎ Easy Download: Download individual images or all at once with file size and savings displayed
- πΌοΈ Sample Image Demo: Load a sample image to try the tool without needing your own files
- Node.js 22+ (see
.nvmrcfor exact version) - yarn or npm
Note: This is a Nuxt 4 application. Make sure you're using compatible tooling.
# Clone the repository
git clone https://github.com/ICJIA/icjia-squish-2026.git
cd icjia-squish-2026
# Install dependencies
yarn install
# Start development server
yarn devThe app will be available at http://localhost:3000
# Generate static site
yarn generate
# Preview production build
yarn preview
# Or build for SSR
yarn build- Nuxt 4 - Latest version of The Intuitive Vue Framework
- Vue 3 - Progressive JavaScript Framework
- Nuxt UI - Fully styled and customizable components
- Nuxt SEO - Complete SEO solution with sitemap, robots, and meta tags
- TypeScript - Type-safe JavaScript
- Tailwind CSS v4 - Utility-first CSS framework
- Canvas API - Client-side image processing
- Vitest - Unit testing framework with coverage reporting
- Try It Out: Click "Load Sample Image" to see the tool in action, or drop your own images
- Drop or Upload: Drag and drop images or click to browse
- Adjust Quality: Use the slider to set compression level (10β100%)
- Preview Comparison: Drag the comparison slider to see the difference; scroll to zoom in for detail
- Download: Save individual images or download all at once β file sizes and savings are shown inline
- Images are processed using the HTML5 Canvas API
- Preserves original format (PNG, JPEG, WebP) with adjustable quality
- Original images remain untouched - new compressed versions are created
- All processing happens client-side for maximum privacy
- Large viewing area: Up to 2000px layout width on wide screens
- 3:2 aspect ratio: Optimal for most images
- Min height: 700px for comfortable viewing
- Interactive slider: Smooth drag-to-compare experience
- Zoom controls: Zoom in/out (100%β800%) with reset button and mouse wheel support
- Pan mode: Click and drag to pan when zoomed above 100%
- Persistent drag: Slider and pan track the mouse even when cursor leaves the image area
- Visual slider: Clear track with current value display
- Quality indicators: Color-coded warnings β green (50%+), yellow (25β49%), red (below 25%) β so you know when quality may suffer
- Real-time stats: See original size, compressed size, and savings percentage inline
- Smart defaults: Starts at 75% quality for optimal balance
The project includes a comprehensive unit test suite with 99 tests covering components and composables.
# Run all tests
yarn test
# Run tests in watch mode
yarn test:watch
# Run with coverage report
yarn test:coverage
# Run component tests only
yarn test:components
# Run composable tests only
yarn test:composables
# Open Vitest UI
yarn test:ui| File | Statements | Branches | Functions | Lines |
|---|---|---|---|---|
| useImageCompression.ts | 100% | 100% | 100% | 100% |
| QualityIndicator.vue | 100% | 100% | 100% | 100% |
| ComparisonSlider.vue | ~79% | 85% | ~60% | ~79% |
| ImageCompressor.vue | ~83% | ~91% | ~25% | ~83% |
Tests use Vitest with happy-dom, Vue Test Utils, and vitest-canvas-mock for Canvas API simulation.
icjia-squish-2026/
βββ app/
β βββ assets/css/
β β βββ main.css # Global styles and theme
β βββ components/
β β βββ ComparisonSlider.vue # Interactive image comparison with zoom/pan
β β βββ ImageCompressor.vue # Main app component
β β βββ QualityIndicator.vue # Quality recommendations
β βββ composables/
β β βββ useImageCompression.ts # Compression logic
β β βββ useDebounce.ts # Debounced function utility
β βββ pages/
β β βββ index.vue # Home page
β βββ app.config.ts # App configuration
β βββ app.vue # Root component
βββ test/
β βββ unit/
β β βββ components/ # Component tests (69 tests)
β β βββ composables/ # Composable tests (30 tests)
β βββ fixtures/ # Mock files and test data
β βββ helpers/ # Test utilities and canvas mocks
β βββ setup.ts # Global test setup
βββ documentation/ # Original Next.js reference code
βββ nuxt.config.ts # Nuxt configuration
βββ vitest.config.ts # Test configuration
βββ package.json # Dependencies
βββ netlify.toml # Netlify deployment config
Main component handling image upload, compression, and state management.
Interactive slider for comparing original and compressed images side-by-side with zoom (100%β800%) and pan controls.
Composable providing image compression functionality using Canvas API.
This project is configured for deployment on Netlify with Nuxt 4:
# Build command
yarn generate
# Publish directory
distThe netlify.toml file is pre-configured with:
- Nitro preset:
netlify-staticfor optimal static site generation - Output directory:
dist(Nuxt 4 custom configuration) - Security headers: CSP (with
object-src 'none',base-uri 'self',form-action 'self'), HSTS, X-Frame-Options, Referrer-Policy, Permissions-Policy - Cache headers: 1-year cache for
/_nuxt/*assets with immutable flag
Set NODE_VERSION=22 and NITRO_PRESET=netlify-static in your Netlify build settings (already configured in netlify.toml).
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the project
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with Nuxt and Nuxt UI
- Inspired by the need for privacy-focused image tools
- Original Next.js implementation available in
documentation/folder
Last audited: 2026-03-27. Full details in CHANGELOG.md.
| Severity | Found | Fixed | Remaining |
|---|---|---|---|
| Critical | 0 | β | 0 |
| High | 0 | β | 0 |
| Medium | 2 | 2 | 0 |
| Low | 8 | 8 | 0 |
All findings resolved. No server-side attack surface (static site). No user data storage or transmission. No runtime CDN dependencies (icons bundled at build time). Web Worker loaded from same-origin static file. All security headers properly configured (CSP, HSTS, X-Frame-Options, Permissions-Policy, Referrer-Policy). Input validation on file types and size. No v-html usage.
| Metric | Result |
|---|---|
| Rules passed | 39/39 |
| Violations | 0 |
| Incomplete | 0 |
| Standards | WCAG 2.0 A/AA, WCAG 2.1 A/AA, Best Practices |
See CHANGELOG.md for a detailed history of changes and version releases.
ICJIA - Illinois Criminal Justice Information Authority
Made with β€οΈ for writers and developers who care about image quality and file size

