Greasy Fork is available in English.
Creates a pause screen before you enter websites with breathing exercises and alternative activities.
// ==UserScript==
// @name Pause Point
// @namespace http://tampermonkey.net/
// @version 1.0.0
// @description Creates a pause screen before you enter websites with breathing exercises and alternative activities.
// @author KHROTU
// @match https://www.youtube.com/*
// @match https://www.reddit.com/*
// @match https://twitter.com/*
// @match https://x.com/*
// @match https://www.instagram.com/*
// @match https://www.tiktok.com/*
// @grant none
// @run-at document-start
// @noframes
// @license MIT
// ==/UserScript==
(function() {
'use strict';
const USER_CONFIG = {
customizations: [
// {
// url: 'https://youtube.com',
// matchType: 'domain', // 'domain' | 'exact' | 'contains'
// altUrls: [
// 'https://en.wikipedia.org',
// 'https://www.google.com',
// 'https://news.ycombinator.com'
// ],
// altActivities: [
// 'Read 10 pages of a book',
// 'Do 20 push-ups',
// 'Write down 3 things you are grateful for',
// 'Organize your desk for 5 minutes'
// ],
// nob: 3, // number of breaths
// customMessage: 'Consider these though',
// enabled: true
// },
// {
// url: 'https://x.com',
// matchType: 'domain',
// altUrls: [
// 'https://github.com',
// 'https://www.duolingo.com',
// 'https://www.coursera.org'
// ],
// altActivities: [
// 'Work on a personal project for 15 minutes',
// 'Meditate for 5 minutes',
// 'Review your goals for the week'
// ],
// nob: 2,
// enabled: true
// }
],
defaultSettings: {
dnob: 3,
dMessage: 'but do you really need to?'
}
};
const INHALE_MS = 2000;
const HOLD_IN_MS = 500;
const EXHALE_MS = 2000;
const HOLD_OUT_MS = 500;
const BREATH_CYCLE_MS = INHALE_MS + HOLD_IN_MS + EXHALE_MS + HOLD_OUT_MS;
const BYPASS_KEY = 'pp_bypass';
const BYPASS_DURATION_MS = 5 * 60 * 1000;
function getConfig() {
const currentUrl = location.href;
const currentHost = location.hostname.replace(/^www\./, '');
let match = null;
for (const c of USER_CONFIG.customizations) {
if (!c.enabled) continue;
const testUrl = c.url.replace(/^www\./, '');
const testHost = new URL(testUrl).hostname.replace(/^www\./, '');
let matched = false;
if (c.matchType === 'exact') {
matched = currentUrl === c.url;
} else if (c.matchType === 'contains') {
matched = currentUrl.includes(testUrl);
} else {
matched = currentHost === testHost || currentHost.endsWith('.' + testHost);
}
if (matched) { match = c; break; }
}
const defaults = USER_CONFIG.defaultSettings || {};
return {
nob: match?.nob ?? defaults.dnob ?? 3,
message: match?.customMessage ?? defaults.dMessage ?? 'but do you really need to?',
altUrls: match?.altUrls ?? [],
altActivities: match?.altActivities ?? [
'Take a short walk',
'Drink a glass of water',
'Write for five minutes',
'Stretch your body',
'Read a book chapter',
'Tidy your workspace'
]
};
}
const bypass = sessionStorage.getItem(BYPASS_KEY);
if (bypass && Date.now() - parseInt(bypass, 10) < BYPASS_DURATION_MS) return;
const cfg = getConfig();
const hostname = location.hostname.replace(/^www\./, '');
const faviconUrl = 'https://www.google.com/s2/favicons?sz=64&domain=' + location.hostname;
const totalBreathMs = cfg.nob * BREATH_CYCLE_MS;
document.documentElement.style.setProperty('display', 'none', 'important');
const takeover = () => {
if (!document.body) {
requestAnimationFrame(takeover);
return;
}
window.stop();
const head = document.head;
const body = document.body;
while (head.firstChild) head.removeChild(head.firstChild);
while (body.firstChild) body.removeChild(body.firstChild);
head.appendChild(Object.assign(document.createElement('title'), {
textContent: 'Pause Point'
}));
const fontLink = document.createElement('link');
fontLink.rel = 'stylesheet';
fontLink.href = 'https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,600;1,400&family=Inter:wght@300;400;500&display=swap';
head.appendChild(fontLink);
const style = document.createElement('style');
style.textContent = `
body {
background: #09090b;
color: #e4e4e7;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
height: 100vh;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.phase {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transition: opacity 0.8s ease;
}
.fluid-wrap {
position: relative;
width: 420px;
height: 420px;
display: flex;
align-items: center;
justify-content: center;
}
.fluid-blob {
position: absolute;
filter: blur(60px);
will-change: transform, opacity;
}
.fluid-blob:nth-child(1) {
width: 260px;
height: 260px;
background: radial-gradient(circle at 30% 30%, rgba(150, 170, 205, 0.38), rgba(120, 140, 180, 0.14));
border-radius: 60% 40% 55% 45% / 55% 45% 60% 40%;
animation: blobDrift1 14s ease-in-out infinite alternate;
}
.fluid-blob:nth-child(2) {
width: 320px;
height: 320px;
background: radial-gradient(circle at 40% 60%, rgba(180, 160, 200, 0.32), rgba(150, 130, 180, 0.12));
border-radius: 45% 55% 40% 60% / 50% 60% 45% 55%;
animation: blobDrift2 18s ease-in-out infinite alternate;
}
.fluid-blob:nth-child(3) {
width: 220px;
height: 220px;
background: radial-gradient(circle at 70% 30%, rgba(160, 195, 180, 0.34), rgba(130, 170, 150, 0.13));
border-radius: 55% 45% 60% 40% / 45% 55% 40% 60%;
animation: blobDrift3 16s ease-in-out infinite alternate;
}
.fluid-blob:nth-child(4) {
width: 280px;
height: 280px;
background: radial-gradient(circle at 50% 50%, rgba(195, 170, 175, 0.28), rgba(170, 145, 150, 0.1));
border-radius: 50% 50% 45% 55% / 55% 45% 50% 50%;
animation: blobDrift4 20s ease-in-out infinite alternate;
}
.fluid-blob:nth-child(5) {
width: 200px;
height: 200px;
background: radial-gradient(circle at 60% 40%, rgba(165, 185, 205, 0.26), rgba(140, 160, 185, 0.09));
border-radius: 48% 52% 58% 42% / 42% 48% 52% 58%;
animation: blobDrift5 12s ease-in-out infinite alternate;
}
@keyframes blobDrift1 { 0% { transform: translate(-10%, -10%) rotate(0deg); } 100% { transform: translate(10%, 10%) rotate(20deg); } }
@keyframes blobDrift2 { 0% { transform: translate(10%, -5%) rotate(0deg); } 100% { transform: translate(-10%, 5%) rotate(-15deg); } }
@keyframes blobDrift3 { 0% { transform: translate(-5%, 10%) rotate(0deg); } 100% { transform: translate(5%, -10%) rotate(25deg); } }
@keyframes blobDrift4 { 0% { transform: translate(5%, 5%) rotate(0deg); } 100% { transform: translate(-5%, -5%) rotate(-20deg); } }
@keyframes blobDrift5 { 0% { transform: translate(0%, -10%) rotate(0deg); } 100% { transform: translate(0%, 10%) rotate(30deg); } }
.breath-ring {
position: absolute;
width: 180px;
height: 180px;
border-radius: 50%;
border: 1.5px solid rgba(220, 225, 235, 0.1);
animation: ringPulse var(--cycle) ease-in-out infinite;
}
.breath-ring:nth-of-type(2) { animation-delay: calc(var(--cycle) * 0.08); width: 220px; height: 220px; }
.breath-ring:nth-of-type(3) { animation-delay: calc(var(--cycle) * 0.16); width: 260px; height: 260px; }
.breath-ring:nth-of-type(4) { animation-delay: calc(var(--cycle) * 0.24); width: 300px; height: 300px; }
@keyframes ringPulse {
0%, 100% { transform: scale(0.72); opacity: 0.1; }
40%, 50% { transform: scale(1.18); opacity: 0.28; }
}
.core-orb {
position: absolute;
width: 140px;
height: 140px;
border-radius: 50%;
background: radial-gradient(circle at 35% 35%, rgba(210, 220, 240, 0.35), rgba(190, 200, 225, 0.12) 55%, transparent 72%);
animation: orbBreathe var(--cycle) ease-in-out infinite;
box-shadow: 0 0 80px rgba(180, 195, 230, 0.18), inset 0 0 50px rgba(210, 220, 240, 0.12);
}
@keyframes orbBreathe {
0%, 100% { transform: scale(0.82); opacity: 0.55; }
40% { transform: scale(1.18); opacity: 1; }
50% { transform: scale(1.18); opacity: 1; }
90% { transform: scale(0.82); opacity: 0.55; }
}
.breath-label {
position: absolute;
font-size: 13px;
letter-spacing: 4px;
text-transform: lowercase;
color: rgba(255, 255, 255, 0.55);
transition: opacity 0.5s ease;
pointer-events: none;
font-weight: 400;
}
.breath-counter {
position: absolute;
bottom: -52px;
font-size: 11px;
letter-spacing: 3px;
color: rgba(255, 255, 255, 0.3);
transition: opacity 0.4s ease;
}
.prompt-wrap {
opacity: 0;
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 24px;
max-width: 640px;
width: 100%;
}
.prompt-header {
display: flex;
align-items: center;
gap: 14px;
flex-wrap: wrap;
justify-content: center;
margin-bottom: 8px;
}
.prompt-favicon {
width: 36px;
height: 36px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.05);
padding: 5px;
box-sizing: border-box;
}
h1 {
font-family: 'Playfair Display', Georgia, 'Times New Roman', serif;
font-size: 34px;
font-weight: 400;
margin: 0;
color: #f0f0f0;
letter-spacing: -0.3px;
line-height: 1.2;
}
.subtext {
font-family: 'Playfair Display', Georgia, 'Times New Roman', serif;
font-size: 20px;
font-style: italic;
color: #71717a;
margin: 6px 0 44px;
}
.alts-label {
font-size: 11px;
text-transform: lowercase;
letter-spacing: 2px;
color: #3f3f46;
margin-bottom: 16px;
}
.alts-grid {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
width: 100%;
max-width: 460px;
list-style: none;
padding: 0;
margin: 0;
}
.alt-item {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 14px 18px;
font-size: 14px;
color: #a1a1aa;
text-decoration: none;
transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
cursor: default;
display: flex;
align-items: center;
justify-content: space-between;
}
.alt-item[href] {
cursor: pointer;
}
.alt-item[href]:hover {
background: rgba(255, 255, 255, 0.07);
border-color: rgba(255, 255, 255, 0.12);
transform: translateY(-1px);
}
.alt-arrow {
opacity: 0;
transition: opacity 0.2s ease;
font-size: 15px;
color: #71717a;
}
.alt-item[href]:hover .alt-arrow {
opacity: 1;
}
.continue-btn {
margin-top: 32px;
font-size: 12px;
color: #3f3f46;
text-decoration: underline;
text-underline-offset: 3px;
cursor: pointer;
background: none;
border: none;
padding: 8px;
transition: color 0.2s ease;
font-family: inherit;
}
.continue-btn:hover {
color: #71717a;
}
`;
head.appendChild(style);
const breathPhase = document.createElement('div');
breathPhase.className = 'phase';
breathPhase.id = 'pp-breath';
const fluidWrap = document.createElement('div');
fluidWrap.className = 'fluid-wrap';
fluidWrap.style.setProperty('--cycle', BREATH_CYCLE_MS + 'ms');
for (let i = 0; i < 5; i++) {
const blob = document.createElement('div');
blob.className = 'fluid-blob';
fluidWrap.appendChild(blob);
}
for (let i = 0; i < 4; i++) {
const ring = document.createElement('div');
ring.className = 'breath-ring';
fluidWrap.appendChild(ring);
}
const core = document.createElement('div');
core.className = 'core-orb';
fluidWrap.appendChild(core);
const breathLabel = document.createElement('div');
breathLabel.className = 'breath-label';
breathLabel.textContent = 'breathe in';
fluidWrap.appendChild(breathLabel);
const breathCounter = document.createElement('div');
breathCounter.className = 'breath-counter';
breathCounter.textContent = '1 / ' + cfg.nob;
fluidWrap.appendChild(breathCounter);
breathPhase.appendChild(fluidWrap);
body.appendChild(breathPhase);
const promptPhase = document.createElement('div');
promptPhase.className = 'phase prompt-wrap';
promptPhase.id = 'pp-prompt';
const header = document.createElement('div');
header.className = 'prompt-header';
const faviconImg = document.createElement('img');
faviconImg.className = 'prompt-favicon';
faviconImg.src = faviconUrl;
faviconImg.alt = '';
header.appendChild(faviconImg);
const h1 = document.createElement('h1');
h1.textContent = 'You are about to visit ' + hostname;
header.appendChild(h1);
promptPhase.appendChild(header);
const sub = document.createElement('p');
sub.className = 'subtext';
sub.textContent = cfg.message;
promptPhase.appendChild(sub);
if (cfg.altUrls.length > 0 || cfg.altActivities.length > 0) {
const label = document.createElement('div');
label.className = 'alts-label';
label.textContent = 'instead, consider:';
promptPhase.appendChild(label);
const grid = document.createElement('ul');
grid.className = 'alts-grid';
for (const url of cfg.altUrls) {
let name;
try { name = new URL(url).hostname.replace(/^www\./, ''); }
catch { name = url; }
const li = document.createElement('li');
const item = document.createElement('a');
item.className = 'alt-item';
item.href = url;
item.target = '_blank';
item.rel = 'noopener noreferrer';
item.appendChild(document.createTextNode(name));
const arrow = document.createElement('span');
arrow.className = 'alt-arrow';
arrow.textContent = '\u2192';
item.appendChild(arrow);
li.appendChild(item);
grid.appendChild(li);
}
for (const act of cfg.altActivities) {
const li = document.createElement('li');
const item = document.createElement('div');
item.className = 'alt-item';
item.appendChild(document.createTextNode(act));
li.appendChild(item);
grid.appendChild(li);
}
promptPhase.appendChild(grid);
}
const continueBtn = document.createElement('button');
continueBtn.className = 'continue-btn';
continueBtn.textContent = 'Continue to ' + hostname;
continueBtn.addEventListener('click', () => {
sessionStorage.setItem(BYPASS_KEY, Date.now().toString());
location.reload();
});
promptPhase.appendChild(continueBtn);
body.appendChild(promptPhase);
document.documentElement.style.display = '';
let breathIndex = 1;
const runBreathLabel = () => {
if (breathIndex > cfg.nob) return;
breathLabel.style.opacity = '0';
setTimeout(() => {
breathLabel.textContent = 'breathe in';
breathLabel.style.opacity = '1';
}, 500);
setTimeout(() => { breathLabel.style.opacity = '0'; }, INHALE_MS);
setTimeout(() => {
breathLabel.textContent = 'breathe out';
breathLabel.style.opacity = '1';
}, INHALE_MS + HOLD_IN_MS + 400);
setTimeout(() => {
breathLabel.style.opacity = '0';
breathIndex++;
if (breathIndex <= cfg.nob) {
breathCounter.textContent = breathIndex + ' / ' + cfg.nob;
}
}, INHALE_MS + HOLD_IN_MS + EXHALE_MS);
};
runBreathLabel();
for (let i = 1; i < cfg.nob; i++) {
setTimeout(runBreathLabel, i * BREATH_CYCLE_MS);
}
setTimeout(() => {
const breath = document.getElementById('pp-breath');
const prompt = document.getElementById('pp-prompt');
if (!breath || !prompt) return;
breath.style.opacity = '0';
setTimeout(() => {
breath.style.display = 'none';
prompt.style.display = 'flex';
requestAnimationFrame(() => {
prompt.style.opacity = '1';
});
}, 800);
}, totalBreathMs - 200);
};
requestAnimationFrame(takeover);
})();