Skip to content

Instantly share code, notes, and snippets.

@dariusk
Last active August 17, 2020 23:48
Show Gist options
  • Save dariusk/4651698 to your computer and use it in GitHub Desktop.
Save dariusk/4651698 to your computer and use it in GitHub Desktop.
This Twine macro lets you make the screen shake! Tested in Chrome and Firefox. Should work in Opera and IE 10+. Uses CSS3 animations. #TwineHacks

This Twine macro lets you make the screen shake! Tested in Chrome and Firefox. Should work in Opera and IE 10+. Uses CSS3 animations, taken from this CSS Reset tutorial.

See a demo in action here.

How to set it up

IMPORTANT NOTE: Due to a bug in Twine 1.3.5, macros do not work on the Start passage. If you want to use this code (or any other macro) on your starting passage, you should put your start passage in a new passage called "ActualStart" and then put the following code in the Start passage:

<<display ActualStart>>

This will warp you directly to ActualStart when your game starts up.

  • Paste the contents of the stylesheet.css file below into a new passage. Call the passage whatever you want, and add the tag "stylesheet" to it.
  • Paste the contents of the script.js file below into a new passage. Call the passage whatever you want, and add the tag "script" to it.
  • Paste the contents of the twineCode file below into your 'ActualStart' passage described above. This will make the screen shake for 5 seconds!

How to use it

Put the following in any passage where you want the screen to shake:

<<screenShake [milliseconds]>>

This tells the screen to shake for a certain number of milliseconds. For example, if you want the screen to shake for 1.5 seconds, put this in your passage:

<<screenShake 1500>>

1000 milliseconds = 1 second, so 1500 milliseconds = 1.5 seconds.

If you set the milliseconds to 0, the screen will shake forever (until the reader moves to a new page)!

function screenShake(time) {
console.log(document);
var el = document.getElementsByClassName('content');
baz = el;
console.log(baz[0]);
el[0].className = el[0].className + ' shake';
if (time > 0) {
setTimeout(function () {
el[0].className = 'content';
}, time);
}
};
// the screenShake macro. Adapted from Emmanuel Turner's article on creating Twine macros. http://eturnerx.blogspot.com/2012/12/how-to-create-custom-macros-in-twine.html
try {
version.extensions['screenShakeMacro'] = {
major: 1,
minor: 0,
revision: 0
};
macros['screenShake'] = {
handler: function (place, macroName, params, parser) {
var time = parseInt(params[0]);
if (typeof time !== 'number') {
time = 1000;
}
// we're overriding the fade function. It behaves as usual except it runs screenShake() if time >= 0.
fade = function (el, options) {
var current;
var proxy = el.cloneNode(true);
var direction = (options.fade == 'in') ? 1 : -1;
el.parentNode.replaceChild(proxy, el);
if (options.fade == 'in') {
current = 0;
proxy.style.visibility = 'visible';
} else current = 1;
setOpacity(proxy, current);
var interval = window.setInterval(tick, 25);
function tick() {
current += 0.05 * direction;
setOpacity(proxy, Math.easeInOut(current));
if (((direction == 1) && (current >= 1)) || ((direction == -1) && (current <= 0))) {
console.log('swapping fader proxy out');
el.style.visibility = (options.fade == 'in') ? 'visible' : 'hidden';
proxy.parentNode.replaceChild(el, proxy);
delete proxy;
window.clearInterval(interval);
if (options.onComplete) options.onComplete();
if (time >= 0) {
screenShake(time);
time = -1;
}
}
};
function setOpacity(el, opacity) {
var percent = Math.floor(opacity * 100);
// IE
el.style.zoom = 1;
el.style.filter = 'alpha(opacity=' + percent + ')';
// CSS 3
el.style.opacity = opacity;
};
};
},
init: function () {},
};
} catch (e) {
throwError(place, "screenShake Setup Error: " + e.message);
}
@keyframes shakeit {
0% { transform: translate(2px, 1px) rotate(0deg); }
10% { transform: translate(-1px, -2px) rotate(-1deg); }
20% { transform: translate(-3px, 0px) rotate(1deg); }
30% { transform: translate(0px, 2px) rotate(0deg); }
40% { transform: translate(1px, -1px) rotate(1deg); }
50% { transform: translate(-1px, 2px) rotate(-1deg); }
60% { transform: translate(-3px, 1px) rotate(0deg); }
70% { transform: translate(2px, 1px) rotate(-1deg); }
80% { transform: translate(-1px, -1px) rotate(1deg); }
90% { transform: translate(2px, 2px) rotate(0deg); }
100% { transform: translate(1px, -2px) rotate(-1deg); }
}
@-o-keyframes shakeit {
0% { -o-transform: translate(2px, 1px) rotate(0deg); }
10% { -o-transform: translate(-1px, -2px) rotate(-1deg); }
20% { -o-transform: translate(-3px, 0px) rotate(1deg); }
30% { -o-transform: translate(0px, 2px) rotate(0deg); }
40% { -o-transform: translate(1px, -1px) rotate(1deg); }
50% { -o-transform: translate(-1px, 2px) rotate(-1deg); }
60% { -o-transform: translate(-3px, 1px) rotate(0deg); }
70% { -o-transform: translate(2px, 1px) rotate(-1deg); }
80% { -o-transform: translate(-1px, -1px) rotate(1deg); }
90% { -o-transform: translate(2px, 2px) rotate(0deg); }
100% { -o-transform: translate(1px, -2px) rotate(-1deg); }
}
@-webkit-keyframes shakeit {
0% { -webkit-transform: translate(2px, 1px) rotate(0deg); }
10% { -webkit-transform: translate(-1px, -2px) rotate(-1deg); }
20% { -webkit-transform: translate(-3px, 0px) rotate(1deg); }
30% { -webkit-transform: translate(0px, 2px) rotate(0deg); }
40% { -webkit-transform: translate(1px, -1px) rotate(1deg); }
50% { -webkit-transform: translate(-1px, 2px) rotate(-1deg); }
60% { -webkit-transform: translate(-3px, 1px) rotate(0deg); }
70% { -webkit-transform: translate(2px, 1px) rotate(-1deg); }
80% { -webkit-transform: translate(-1px, -1px) rotate(1deg); }
90% { -webkit-transform: translate(2px, 2px) rotate(0deg); }
100% { -webkit-transform: translate(1px, -2px) rotate(-1deg); }
}
@-moz-keyframes shakeit {
0% { -moz-transform: translate(2px, 1px) rotate(0deg); }
10% { -moz-transform: translate(-1px, -2px) rotate(-1deg); }
20% { -moz-transform: translate(-3px, 0px) rotate(1deg); }
30% { -moz-transform: translate(0px, 2px) rotate(0deg); }
40% { -moz-transform: translate(1px, -1px) rotate(1deg); }
50% { -moz-transform: translate(-1px, 2px) rotate(-1deg); }
60% { -moz-transform: translate(-3px, 1px) rotate(0deg); }
70% { -moz-transform: translate(2px, 1px) rotate(-1deg); }
80% { -moz-transform: translate(-1px, -1px) rotate(1deg); }
90% { -moz-transform: translate(2px, 2px) rotate(0deg); }
100% { -moz-transform: translate(1px, -2px) rotate(-1deg); }
}
.shake {
-webkit-animation-name: shakeit;
-webkit-animation-duration: 0.8s;
-webkit-transform-origin:50% 50%;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
-moz-animation-name: shakeit;
-moz-animation-duration: 0.8s;
-moz-transform-origin:50% 50%;
-moz-animation-iteration-count: infinite;
-moz-animation-timing-function: linear;
-o-animation-name: shakeit;
-o-animation-duration: 0.8s;
-o-transform-origin:50% 50%;
-o-animation-iteration-count: infinite;
-o-animation-timing-function: linear;
animation-name: shakeit;
animation-duration: 0.8s;
transform-origin:50% 50%;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
.shake{
display:inline-block
}
THIS PASSAGE IS SHAKING FOR TWO SECONDS, WHOAAAAA!!!!
<<shakeScreen 2000>>
@ohiofi
Copy link

ohiofi commented May 20, 2014

Love this macro, but unfortunately it stopped working when I updated to 1.3.5
I am not placing the macro in the Start passage.

Here is what I'm getting: http://ohiofi.com/screenshaketest.html

Here is the Twine file: http://ohiofi.com/screenshaketest.tws

Also, I noticed a typo in the twineCode section of this page. You have...

THIS PASSAGE IS SHAKING FOR TWO SECONDS, WHOAAAAA!!!!

<<shakeScreen 2000>>

but it SHOULD say...

THIS PASSAGE IS SHAKING FOR TWO SECONDS, WHOAAAAA!!!!

<<screenShake 2000>>

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment