bad-apple-console 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -0
- package/build.js +59 -0
- package/frames.js +6575 -0
- package/index.html +95 -0
- package/index.js +94 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Bad Apple! Console Player
|
|
2
|
+
|
|
3
|
+
Play the Bad Apple! music video inside Chrome DevTools at 30fps.
|
|
4
|
+
|
|
5
|
+
No canvas. No runtime image loading. No flashing. Just 6,572 pre-computed frames played seamlessly through `console.log` CSS backgrounds.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install bad-apple-console
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or drop the files onto any static host:
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<script type="module" src="./index.js"></script>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
Import the player in your project:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
import { startPlayback } from 'bad-apple-console';
|
|
29
|
+
|
|
30
|
+
// Playback starts automatically when DevTools is opened.
|
|
31
|
+
// Or call it manually:
|
|
32
|
+
startPlayback();
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Open Chrome DevTools (`F12` or `Cmd+Option+J`) and the video will start automatically.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## How It Works
|
|
40
|
+
|
|
41
|
+
1. **Pre-build step** (`build.js`): All 6,572 PNG frames are resized to 120x90, converted to base64, and the exact `console.log('%c ', ...)` CSS strings are pre-computed into `frames.js`.
|
|
42
|
+
2. **Zero runtime overhead**: At playback, the browser simply cycles through pre-loaded strings. No `Image`, no `Canvas`, no base64 conversion, no CORS.
|
|
43
|
+
3. **Smooth 30fps**: `requestAnimationFrame` with elapsed-time tracking ensures exact frame timing.
|
|
44
|
+
4. **Console-only trigger**: The player detects DevTools opening via window dimension changes and the `console.profile()` timing trick. Playback does **not** start from clicks or keystrokes.
|
|
45
|
+
5. **No flashing**: Frames scroll through console history instead of replacing each other, avoiding the nausea-inducing `console.clear()` strobe effect.
|
|
46
|
+
6. **Scaled display**: The 120x90 frames are displayed at 480x360 via CSS padding so they fill the console width.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## DevTools Detection
|
|
51
|
+
|
|
52
|
+
The player uses two methods to detect when Chrome DevTools is opened:
|
|
53
|
+
|
|
54
|
+
- **Window dimension check**: Compares `window.outerWidth` vs `window.innerWidth`. When DevTools is docked, the viewport shrinks.
|
|
55
|
+
- **`console.profile()` timing trick**: DevTools instrumentation slows down `console.profile()` measurably. If the call takes >100ms, DevTools is active.
|
|
56
|
+
|
|
57
|
+
If detection fails (e.g., undocked DevTools on a separate monitor), refreshing the page with DevTools already open will trigger playback immediately.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Building from Source
|
|
62
|
+
|
|
63
|
+
If you want to regenerate `frames.js` from your own frame sequence:
|
|
64
|
+
|
|
65
|
+
1. Place your numbered PNG frames (`output_0001.png`, `output_0002.png`, ...) in the project directory.
|
|
66
|
+
2. Install dependencies:
|
|
67
|
+
```bash
|
|
68
|
+
npm install
|
|
69
|
+
```
|
|
70
|
+
3. Run the build:
|
|
71
|
+
```bash
|
|
72
|
+
npm run build
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The build script uses `jimp` to resize, base64-encode, and pre-compute CSS strings for every frame. It outputs a single `frames.js` module.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## File Sizes
|
|
80
|
+
|
|
81
|
+
- `frames.js`: ~31 MB (6,572 pre-computed frames at 120x90)
|
|
82
|
+
- Source PNGs: ~133 MB (not included in npm package)
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
MIT
|
package/build.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Jimp } from 'jimp';
|
|
2
|
+
import { readdir, writeFile } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
const FRAME_DIR = '/Users/samuelebertocco/Desktop/bad apple ';
|
|
6
|
+
const OUTPUT_FILE = '/Users/samuelebertocco/Desktop/bad apple /frames.js';
|
|
7
|
+
const TARGET_WIDTH = 120;
|
|
8
|
+
const TARGET_HEIGHT = 90;
|
|
9
|
+
|
|
10
|
+
async function build() {
|
|
11
|
+
const files = await readdir(FRAME_DIR);
|
|
12
|
+
const pngFiles = files
|
|
13
|
+
.filter(f => f.match(/^output_\d+\.png$/))
|
|
14
|
+
.sort((a, b) => {
|
|
15
|
+
const numA = parseInt(a.match(/\d+/)[0], 10);
|
|
16
|
+
const numB = parseInt(b.match(/\d+/)[0], 10);
|
|
17
|
+
return numA - numB;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
console.log(`Found ${pngFiles.length} frames`);
|
|
21
|
+
|
|
22
|
+
const frames = [];
|
|
23
|
+
|
|
24
|
+
for (let i = 0; i < pngFiles.length; i++) {
|
|
25
|
+
const file = pngFiles[i];
|
|
26
|
+
const image = await Jimp.read(join(FRAME_DIR, file));
|
|
27
|
+
|
|
28
|
+
// Force resize to exactly 120x90
|
|
29
|
+
image.resize({ w: TARGET_WIDTH, h: TARGET_HEIGHT });
|
|
30
|
+
|
|
31
|
+
// Get base64 PNG data URL
|
|
32
|
+
const base64 = await image.getBase64('image/png');
|
|
33
|
+
|
|
34
|
+
// Pre-compute the exact console.log CSS string
|
|
35
|
+
const style = [
|
|
36
|
+
'font-size: 1px;',
|
|
37
|
+
`padding: ${TARGET_HEIGHT / 2}px ${TARGET_WIDTH / 2}px;`,
|
|
38
|
+
`background: url(${base64}) no-repeat;`,
|
|
39
|
+
'background-size: contain;',
|
|
40
|
+
'color: transparent;'
|
|
41
|
+
].join(' ');
|
|
42
|
+
|
|
43
|
+
frames.push(style);
|
|
44
|
+
|
|
45
|
+
if ((i + 1) % 500 === 0 || i === pngFiles.length - 1) {
|
|
46
|
+
console.log(`Processed ${i + 1}/${pngFiles.length} frames`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Write frames as a JS module
|
|
51
|
+
const lines = frames.map(f => ` "${f}"`);
|
|
52
|
+
const output = `export const FRAMES = [\n${lines.join(',\n')}\n];\nexport const TOTAL_FRAMES = ${frames.length};\n`;
|
|
53
|
+
|
|
54
|
+
await writeFile(OUTPUT_FILE, output);
|
|
55
|
+
|
|
56
|
+
console.log(`Done! Wrote ${frames.length} pre-computed frames to ${OUTPUT_FILE}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
build().catch(console.error);
|