marinettiJS
Motion: phenomenal and literal
The Italian Futurists revered the dynamic sensation of speed and motion. But their work did not employ actual motion in the manner of a Jean Tinguely drawing machine sculpture or a Jenny Holzer installation — although to be fair, those artists owe a formal debt to the Futurist obsession with phenomena.
Nevertheless, how strange indeed it was that Marinetti would devote himself to such a static medium as print:
The program of reform Marinetti called for was in some ways more radical than the work he achieved … . Marinetti’s well-documented infatuation with the concept of a modernity defined as speed, simultaneity, and sensation … leads to a paradox of major proportions when the temporally stable, static, and resolutely referential forms of printing on the page are used to invoke the immediacy and temporally ephemeral staging of phenomenal experience.
Johanna Drucker, The Visible Word: Experimental Typography and Modern Art, 1909-1923
Or was it so strange after all? Remember Marinetti was practicing in the early 20th Century, decades before Tinguely and a century before Holzer. If he had had access to the high-lumen long-throw projector used for Holzer’s Guggenheim installation, would he have used it? This exercise assumes: Absolutely!
The poem we forged in CSS from the previous exercise is filled with an implied, phenomenal motion. Marinetti compels us to imagine the VENT and SANG forms pumping like the pistons referenced in the first “line” of the text. If Marinetti had access to HTML and CSS, he would certainly have become a web designer.
But web design is a stool with 3 legs, not 2. He would not have ignored the Javascript programming language that provides the web with time, motion, and interactivity.
So: let’s take our forgery and make literal motion out of the implied dynamic forces present in the text!
Animation and interactivity
In this exercise, we introduce JavaScript, or JS for short. JS is the programming language for the web. Although it has a fairly forgiving syntax (which some hardcore programmers despise!), it is fundamentally less like HTML and CSS — which are markup languages — and more like Python without the stricter syntax. It’s essentially an object-oriented programming language, meaning it is used to manipulate modular “objects” like variables, data structures, functions, and methods. Already you might feel lost, right? Welcome to the club.
For artists, it’s less critical that we know how to build a JS script from scratch such as we must with markup languages. Instead, we just need to know enough about the language to work with a script library or a code snippet. In this exercise, we’ll do just that: we’ll even use AI to help us generate a useful script!
Animation is coded in two ways. The first kind of animation code uses HTML and CSS alone. The second kind adds JS, introducing interaction activated by the client, through input like tapping on a screen or clicking with a mouse.
Animations also come in two CSS flavors: @keyframes and transitions. If you come to CSS from a traditional video or animation time-and-motion praxis, @keyframes can be less intuitively understood than transitions. Strictly speaking, CSS keyframing is just a loose analogy to actual keyframing. It’s confusing, but worth noting, that @keyframes does NOT determine the duration, but merely how a duration called as a property in a CSS stack is divided up, usually (but not always) by percentages.
You can do keyframing or transitioning using HTML/CSS alone or in combination with JS. In this exercise, we’ll do one animation example keyframing with HTML/CSS alone, and we’ll explore a sample of transition-based animation with JS.
CSS animation resources
Although many CSS properties (opacity, color, or blend-modes, for example) can be animated, we see that transform properties are the backbone of our Marinetti web animation. For a sense of what’s possible with transforms and animation, visit Sunshine in My Throat, the blog of media artist Rosa Menkman.
A raft of helper sites to explore:
- web code tools | A sandbox for generating code, which you can copy/paste right into your files.
- westciv Transforms | Another sandbox, this one with sliders, emphasizing transforms. An oldie but goodie.
- web.dev Animations | Explainers and sandboxes written by members of the Chrome team.
- web.dev Transitions | Same site, but emphasizing non-keyframed animation.
- An Interactive Guide to CSS Transitions | By Josh Comeau, this has some great explainers on the sometimes tough-to-grasp concept of easing.
- CodePen | The mother of all sandboxes and demos, this social development environment supports a generous culture of sharing.
- Animate.css | A CDN library of ready-to-use, cross-browser animations for use in your web projects. You can install it right into your webpage in a <link>. Saves development time, server space, and speed of page load.
Dozens more can be found using a keyword search in a search engine!
JS interactivity resources
All kinds of client input, from hovering to click-and-drag action to randomizing links, can be programmed to generate interactivity. We’ll see clicking or tapping will activate the animation of two elements in our Marinetti file. One reason we avoid hover effects (sometimes known as mouse-overs) here is their incompatibility with mobile browser input behaviors.
Many sources of scripts exist in the vast web developer community. For examples of JS capabilities visit the demo sites at EaselJS, TweenJS, SoundJS, and PreloadJS.
A gaggle of helper sites to visit:
- Learn Javascript | A series of tutorials in a terrific UX.
- Javascript Code Generator | An oldie but goodie for some standard JS functions like generating a random string or page redirect. Good for learning.
- Codepen | As good for JS as it is for CSS.
- JSFiddle | Another sandbox, linked heavily with the Github and StackOverflow communities.
- Github | “Git” stuff like the incredible threejs materials for displaying 3D content and other cutting-edge projects.
- StackOverflow | A troubleshooting community where every question about coding has some kind of discussion raging.
- threejs | A JS library mashing up my two geeky pleasures: 3D modeling and web design!
- CreateJS | Modular JS libraries enabling rich interactive content. Can download and host on your browser, or install via CDN <link>.
- AI Code Generators for Javascript | Sourceforge’s list of the hottest trend in JS development: the use of AI! Some free browser-based options listed include SourceGraph Cody, Codeium, and ChatGPT.
… and a ton of others too numerous to mention, available via a search engine.
Animating Marinetti in CSS and JS
Take a look at the finished product first, embedded below. You’ll see VENT and SANG pumping along like pistons in an engine, in a perpetual loop. Now, tap or click on the MOUVEMENT and Karazouc-zouc-zouc texts and you’ll see them shake and rotate in an exaggerated manner. Movement was the Italian Futurist’s first love, so these animations strongly support the content. They aren’t just bells and whistles to attract attention — a critique often justifiably directed at web animation.
F. T. Marinetti, excerpt from Dunes, 1914, animated by williamCromar, 2023
Take your static Marinetti exercise, and duplicate the HTML and CSS files to create an animated version using the steps below. It’s a good idea as you work to look at the source code for the sample project:
Step 1: Transforms for body text
Start by animating the negateur and affirmateur text blocks with their curly braces, as seen here:
The code for the animation properties applied to each declaration is quite similar …
animation-name: compress1;
animation-duration: 1s;
animation-iteration-count: infinite;
… but the animation-name
property will be compress1
under #nega
and compress2
for #affi
, because we will see that these have to be unique: they need to do opposite things simultaneously. The 1s
duration was chosen because it’s reasonably quick but not so fast as to be illegible. The iteration count set for infinite
creates a loop.
Meanwhile, in the CSS file, we’ve created a space for the @keyframes
. These look like this for
and compress1
compress2
respectively:
@keyframes compress1 {
0% {transform: scale(1,1);}
50% {transform: scale(0.5,1); left:95px;}
100% {transform: scale(1,1);}
}
@keyframes compress2 {
0% {transform: scale(0.5,1);left:95px;}
50% {transform: scale(1,1);left:60px;}
100% {transform: scale(0.5,1);left:95px;}
}
Look carefully at the scale functions and you’ll see they are opposites. This is what allows one to be fully compressed while the other is not. Perhaps the trickiest part was figuring out the left positioning to provide an illusion of a static right-hand side. No real secret here, just a lot of trial and error with the values until it looked correct!
YOUR TURN: Apply the first animation to your code. You may copy-paste our code, but play around with some of the values so you can see why they work.
Step 2: Transforms for headline text
To make the first step look convincing, we need an alternate animation for the vent and sang divs, as seen here:
Whereas the first set of divs are compressing, these are expanding. As with the first step, these look pretty similar …
animation-name: expand1;
animation-duration: 1s;
animation-iteration-count: infinite;
… with expand1
assigned to #vent
and expand2
assigned to #sang
.
Compare the @keyframes
in the CSS with those of the first step:
@keyframes expand1 {
0% {transform: scale(1,1);}
50% {transform: scale(2,1);left:40px;}
100% {transform: scale(1,1);}
}
@keyframes expand2 {
0% {transform: scale(2,1);left:40px;}
50% {transform: scale(1,1);left: 2px;}
100% {transform: scale(2,1);left:40px;}
}
And again, there’s no magic bullet here, just a lot of grunt work getting the numbers right. However, it is insightful to note that we encountered a bug when we assigned expand1
to #vent
. When we applied it, there was a hiccup in the animation, unlike the smooth code for expand2
at #sang
.
YOUR TURN: Apply the second animation to your code. You may copy-paste our code, but play around with values. Note also the bug that emerges in VENT!
Debugging happens
We interrupt this exercise for a PSA!
Let us here reinforce a basic premise of coding:
DEBUGGING IS PART OF THE PROCESS
There is not a coder on earth who has not encountered an unexpected result in their code. So embrace the reality that debugging happens. Plan for it. Manage time to include it.
And computers are pretty stupid. Unlike humans, they can’t infer what the code author means when they make a small error. A computer just sits there like a brick, offering no help whatsoever. OK, back to the exercise…
A debugging workflow
Frustratingly, one of the animations is smooth, the other is not. So, how did we fix the bug in our animation?
- First, look for typos, the most common source of an infestation. Especially common is the mistyping of a
O
for a0
or vice-versa — monotype text will signal the zero with a line. A lower-casel
can look like a1
or an upper-caseI
: monotype differentiates them, too, but they’re hard to catch. In our search, we found no typos. - Next, explore syntax errors. Often it can be a missing
'
, a mistyped"
, or a:
instead of a;
. Dreamweaver will often alert you for errors, but the warnings can be jargon-laden. We found no missing brackets or misplaced declarations or values in our search. - Next, we look for differences. How are these circumstances different?
#vent
contains a nested div, but#sang
does not.#vent
starts by expanding, but#sang
does not.- Isolate differences by using comments to disable code, a process known as commenting out. Comments are a debugger’s best friend because you can “delete” without losing something, possibly isolating the source of the bug.
- Commenting on the nested div does not eliminate the bug.
- So: we conclude that starting by expanding doesn’t work!
How to solve that? We see that VENT is “ungluing” itself from the left side when the animation starts.
Perhaps we should try forcing it to stick to the left margin? We appended the following to the existing ID style:
#vent {
......
left: 0px;
......
}
Voilà! Adding the left
property with a value 0px
tells the browser “I want this thing to stick to the left side.” Notice we create a comment explaining our fix, both for others who inspect the code and for ourselves as a sanity tactic. The comment acts like a kind of sticky-note reminder.
A debugging mindset
I hasten to point out that it was not an immediately gratifying process as outlined. There are several strategies one should consider when frustration sets in:
- Step away. Sometimes leaving the problem alone for a spell allows your brain to connect dots it can’t while you’re in the midst of self-targeted code-rage.
- Talk to the duck. Many coders have had the experience of explaining a problem to someone else and then discovering the solution in the process of explaining the problem. The “duck” is an inanimate object you force yourself to talk out loud to without bothering someone else (who might be busy with a bug of their own!).
- Ask the hive mind. Visit StackOverflow or other chats with a keyword search for your problem.
YOUR TURN: Debug your VENT animation, using our values if you wish.
And with the conclusion of our debugging episode, the constant piston action is done! Now for the interactive part.
Step 3: Creating a shaking script using animation
and AI
First-time with JS
Now is when the insecurities begin for most artists! Scripts are often shockingly left-brained and seemingly human-illegible. But we have several things going for us:
- We’re already fairly familiar with animation CSS from our experience in this and other exercises, and our script will activate such an animation, so most of the heavy lifting is done in a familiar markup language.
- The color-coding one finds in Dreamweaver and at helper sites like JSFiddle will help us understand what we should and should not do with a script.
- We don’t need to write the JS from scratch. Most web developers work with code snippets. Such reusable blocks of code are shared on the web, can be seen by inspecting a site’s source code, or exist as part of a CDN library.
- We can use AI to help us with the basic structure of a script!
Now, it certainly helps to know something about programming. If you don’t know the difference between a string and an integer, it can become overwhelming immediately. And if you don’t know basic JS syntax, you won’t know how to modify any script you might adapt to your needs. In this exercise, we recommend Learn Javascript as the place to become familiar with the language. Even if you use AI to generate a script, which we will do in this step, you need to know what you can and cannot tweak to make it work for your specific circumstance. That being said, we’ve chosen to develop some fairly simple scripts to de-escalate the learning curve here.
Shake it up
Open the source code at this link to view our work here:
The code we are adding goes in the following places:
-
HTML
-
CSS
-
JS
<div id="mouv" class="borderRight "> <div id="mouvText" class="mouvStyle centerText"> <!-- ADD NESTED DIV TO APPLY SHAKE --> <div class="mouvShake"> mouvement<br /> de<br /> 2 pistons </div> </div> </div> ......
And after the container but before the body close (note the spelling of script with a j, for site security purposes:
</container> <!-- ADD SHAKE SCRIPT HERE --> <scrjpt type="text/javascript" src="js/shake.js"></scrjpt> </body> </html>
.mouvShake { cursor: pointer; user-select: none; } .ease-shake { animation: easeShake 0.2s ease-in-out infinite; } @keyframes easeShake { 0%, 100% { transform: translateX(0) rotate(0deg); } 15% { transform: translateX(-30px) rotate(-10deg); } 30% { transform: translateX(15px) rotate(10deg); } 45% { transform: translateX(-10px) rotate(-5deg); } 60% { transform: translateX(-20px) rotate(-15deg); } 75% { transform: translateX(20px) rotate(20deg); } 90% { transform: translateX(-10px) rotate(-15deg); } }
document.addEventListener('DOMContentLoaded', function () { const mouvShake = document.querySelector('.mouvShake'); let isShaking = false; mouvShake.addEventListener('click', function () { if (!isShaking) { isShaking = true; mouvShake.classList.add('ease-shake'); // Reset and allow replay after 30 seconds setTimeout(function () { mouvShake.classList.remove('ease-shake'); isShaking = false; }, 30000); } }); });
We used ChatGPT to generate the basic scheme of this code. To view the conversation, visit this link:
When it first launched a little more than a year before this writing, ChatGPT sounded too good to be true. While most of our academic colleagues looked on with a sense of apocalyptic horror, I was thinking, “Wait a minute… can this thing generate code for free? Did I just get 20 years of my life back?”
So when it came time to update this exercise, I wanted to see if it could tackle the task of generating our first script: to shake the mouvement text when clicked or tapped.
At first, I was going to feed it a little code as a prompt, but I decided to be more narrative — even a little vague — to see how it would respond. I also knew it tended to respond better if you framed a persona for it. So I asked it:
You are writing a program in Javascript for a div with the class mouvShake. When the text content of the div is clicked or tapped, it will shake vigorously, and taper down to a stop after 30 seconds using ease-out.
You can see in the conversation linked above that it proactively fed me some HTML with actual content I did not ask for, and some CSS keyframes, because it clearly understands these things work together. Then it explains how it works to me — again, something super helpful I did not ask it for!
But, it didn’t fully understand what I wanted. When I implemented the code, the mouvement text lumbered slowly back and forth like a depressed tennis ball. When it reset the animation to remove the shake, that was a permanent state: we couldn’t repeat the motion with another click. So, I went back to the conversation a few times, refining my request. While ChatGPT never quite got me to the random, vigorous shake I wanted, it gave me enough of a structure that I could fine-tune the keyframes and get exactly what I had in mind. Compare what it wrote for me with how I tweaked it, mostly by changing the keyframe frequency and values.
The moral? Using AI is a MASSIVE timesaver, but it is NOT a substitute for understanding code! I still needed to intervene to get exactly what I wanted. And I also had to correct it on one huge point: best practice is now to place the script after the content, not in the head! That said, it built a pretty flawless script here.
YOUR TURN: Create a script and apply it to mouvement. You may copy-paste our solution, or you can try your hand at working with AI to generate code you can copy-paste and tweak in your code.
Step 4: Creating a spin script using transition
and a helper site
A more traditional workflow (for me at least) has been to dig deep into the hive mind and come up with usable solutions. This is actually what AI does, only it can do it orders of magnitude faster than I can.
Having said that, I think there’s still a place for exploration and discovery when you’re visiting a helper site. I can’t tell you how many amazing things serendipity has sent my way doing this. As I’m looking for a solution to Thorny Problem A, I run across Cool Opportunity B, C, D, and E, storing those away for future use. As much as I was impressed by the quickness of the AI-generated build, I absolutely won’t abandon my forays into StackOverflow and CodePen.
So in this instance, we’re going to visit CodePen to see if there’s a solution to creating a spin without using animation
keyframes, instead looking for a light-weight transition
effect to do it.
Crack open the source code in our final step to see our work:
I logged into CodePen and did a keyword search for “rotate animate” and found the following Pen:
It was a little underwhelming but I knew I could expand this into what I needed. I just needed to test changing some values to confirm it. It’s lovely to play with the CodePen sandbox! In the CSS, I found the transition
property and changed that to a massive 30s
, then changed the rotation to a crazy 360000deg (basically spinning 1000 times in 30 seconds!). It acted like an airplane propeller starting up — exactly what I wanted!
So now we adapt all the function and class names to our code. Here’s how it translated:
CODEPEN JS
function chevronToggle() {
const icon = document.querySelector('.fa-chevron-down');
icon.classList.toggle('reverse');
}
MARINETTI JS
function karaToggle() {
const spin = document.querySelector('.karaSpin');
spin.classList.toggle('reverse');
}
karaToggle();
- The name of the function chevronToggle in CodePen made no sense for our context, so we changed it to karaToggle. (Since we placed this in its own JS file, we also added an empty function call at the end to keep Dreamweaver’s function-defined-but-never-used error at bay — of course, it will be used, but in the HTML).
- We changed the object icon to a more logical spin.
- The class .fa-chevron-down makes no sense for our file, so it became .karaSpin.
- The class we kept — reverse — still made sense in our scheme so it remained the same.
In the CSS, we didn’t need a body
style, nor did we require a container
, so we didn’t use any of that. For what remained, here’s what changed:
CODEPEN CSS
.fa-chevron-down {
font-size: 4em;
color: #00C957;
transition: ease-in-out .3s;
&:hover {
cursor: pointer;
}
}
.reverse {
transform: rotate(180deg);
}
MARINETTI CSS
.karaSpin {
transition: ease-in-out 30s;
}
.reverse {
transform: rotate(360000deg);
}
- We didn’t need
font-size
orcolor
, nor did we need the&:hover
pseudo-class to change the style of the cursor. We only needed to change the class from.fa-chevron-down
to.karaSpin
and increase the duration from.3s
to30s
. - In the
reverse
class, we changed the rotation value from180deg
to360000deg
to radically increase the number of spins.
In HTML, we ignored the container and just needed to apply the class and the click action:
CODEPEN HTML
<i class="fa fa-chevron-down" onclick="chevronToggle()">
MARINETTI HTML
<div class="karaSpin" onclick="karaToggle()">
The class assigned in Codepen (containing a harmless typo, fa, which we ignored) was changed from fa-chevron-down
to karaSpin
, and the onclick
value revised from chevronToggle()
to karaToggle ()
.
So when we’re adapting a code solution from a helper site, we see that we need two things:
- Enough skill with JS that we know the syntactic location of functions and classes that we need to change to fit our code.
- Enough skill with CSS and HTML that we can figure out what to keep, what to toss, and what to change.
- The ability to abstract: that is, to mentally map the code from the helper site onto our site.
YOUR TURN: Create a script and apply it to Karazouc. You may copy-paste our solution, or you can try your hand at working with a helper site to generate code you can copy-paste and map onto your code.
SFTP this!
SFTP upload your animated poem with interaction! Congratulations: you’ve successfully mastered the HTML-CSS-JS boot camp!