Last active
June 16, 2025 13:18
-
-
Save sophiawisdom/c1b16fcaca017d1aec2358c6fb619697 to your computer and use it in GitHub Desktop.
Screenshot hoster. Download these two files, put your screenshots in the same directory, run lister.py, and you have a fun website that shows all your screenshots.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<title> vibe images <!-- change this to whatever you want! --> </title> | |
<!-- Add any text here if you so desire; the images won't be drawn over it. --> | |
<div id="container"> | |
</div> | |
<script> | |
function shuffleArray(array) { | |
for (let i = array.length - 1; i > 0; i--) { | |
const j = Math.floor(Math.random() * (i + 1)); | |
[array[i], array[j]] = [array[j], array[i]]; | |
} | |
return array; | |
} | |
fetch("image_widths_heights.json").then(f => { | |
if (f.status == 404) { | |
alert("unable to find image_widths_heights.json -- you should run lister.py to create it."); | |
} | |
return f.json() | |
}).then(data => { | |
let key_values = shuffleArray(data); | |
const rect = container.getBoundingClientRect(); | |
let screen_width = rect.width; | |
// By default I will have a lot of images that are just enormous and take up most of the screen. Here I attempt | |
// to resize the images to be approximately a similar size. | |
// To adjust image size, change goal_pixels here. | |
const pairs = key_values.map(([key, [w, h]]) => { | |
let goal_pixels = 500*300; | |
let actual_pixels = w*h; | |
if (actual_pixels/goal_pixels > 16) { | |
return [parseInt(w/8), parseInt(h/8)]; | |
} | |
if (actual_pixels/goal_pixels > 4) { | |
return [parseInt(w/4), parseInt(h/4)]; | |
} | |
return [parseInt(w/2), parseInt(h/2)]; | |
}).map(([w, h]) => [(w+10) > screen_width ? screen_width-10 : w, h]); | |
// gather all the text nodes so the images don't intersect them | |
const textNodes = []; | |
const treeWalker = document.createTreeWalker( | |
document.body, | |
NodeFilter.SHOW_TEXT, | |
{ acceptNode: (node) => node.textContent.trim() ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT } | |
); | |
while(treeWalker.nextNode()) textNodes.push(treeWalker.currentNode); | |
// Now we attempt to place images. I looked at some actually good algorithms but decided to instead use a stupid | |
// algorithm: for each image, attempt to place it in a random location up to height 600. if it intersects with | |
// any other images, try again. If after 50 attempts at random placing we don't manage to place it, increase the | |
// height by 50. | |
let positions = []; | |
let i = 0; | |
// this could be a for loop, but i thought it would be fun to have them appear gradually. | |
let interval = setInterval(() => { | |
let height = 600; | |
let [w, h] = pairs[i]; | |
while (1) { | |
let found_place = false; | |
let w_max = screen_width-w; | |
let h_max = height-h; | |
for (let _ = 0; _ < 50; _++) { | |
let a_left = Math.random() * w_max; | |
let a_top = Math.random() * h_max; | |
let a_right = a_left+w; | |
let a_bottom = a_top+h; | |
// do intersection checks against images | |
let intersects = false; | |
for (let j = 0; j < positions.length; j++) { | |
let [b_left, b_top] = positions[j]; | |
let b_right = b_left + pairs[j][0]; | |
let b_bottom = b_top + pairs[j][1]; | |
if (a_left < b_right && a_right > b_left && a_bottom > b_top && a_top < b_bottom) { | |
intersects = true; | |
break; | |
} | |
} | |
// do intersection checks against text. TOCONSIDER: cache bounding rect calculation? | |
for (const node of textNodes) { | |
const range = document.createRange(); | |
range.selectNodeContents(node); | |
const rect = range.getBoundingClientRect(); | |
let node_top = rect.top + window.pageYOffset; | |
let node_left = rect.left + window.pageXOffset; | |
if (a_left < (node_left+rect.width) && a_right > node_left && a_bottom > node_top && a_top < (node_top+rect.height)) { | |
intersects = true; | |
break; | |
} | |
} | |
if (!intersects) { | |
found_place = true; | |
positions.push([parseInt(a_left), parseInt(a_top)]); | |
break; | |
} | |
} | |
if (found_place) { | |
break; | |
} else { | |
height += 50; | |
} | |
} | |
// Create the image | |
let img = document.createElement("img"); | |
img.src = key_values[i][0]; | |
img.width = pairs[i][0]; | |
img.height = pairs[i][1]; | |
img.style.width = pairs[i][0]; | |
img.style.height = pairs[i][1]; | |
img.style.position = "absolute"; | |
img.style.top = positions[i][1]; | |
img.style.left = positions[i][0]; | |
img.loading = "lazy"; | |
container.appendChild(img); | |
i++; | |
if (i == key_values.length) { | |
clearInterval(interval); | |
} | |
}, 100); | |
}) | |
</script> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import json | |
try: | |
from PIL import Image | |
from pillow_heif import register_heif_opener | |
register_heif_opener() | |
except ImportError as e: | |
print("Got import error", e) | |
print("You need to install pillow and pillow-heif: `pip3 install pillow pillow-heif`") | |
import sys; sys.exit(1); | |
files = [] | |
for file in os.listdir("."): | |
try: | |
im = Image.open(file) | |
files.append([file, [im.width, im.height]]) | |
except: # e.g. .DS_Store, calculater.py, file | |
continue | |
json.dump(files, open("image_widths_heights.json", 'w')) | |
print(f"Successfully created image_widths_heights.json with {len(files)} files.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment