Posted on

Live Web ⁄ Class 1 ⁄ Self Portrait

[Live url here]

My self portrait consists of a series of videos, which together compose an image of my head, that collectively follow the cursor as it moves left and right across the window. In order to achieve this, I first recorded a video of myself turning from one profile position to the other. The video lasts about 25 seconds. After recording I split the video vertically into 10 even pieces and added them to the DOM as 10 separate <video> elements. Then I used js to map the length of the video in seconds to the width of the window in pixels, and had the video fast-forward or rewind to the time that corresponds to the current mouse position, every time mouse movement is detected.

I split the portrait into several pieces so that I could play around with the variables that determine the number of frames and the time each video transition would take to fast-forward/rewind from one point to another. I like the resulting jumpy effect and the way my face gets abstracted at certain moments and then slowly pieces back together.

The html/css…

<html>
	<head>
		<script type="text/javascript" src="main.js"></script>
	</head>
	<body>
	<br>
	<br>
	<div id="2">
		<video id="vid11" src="vid/Comp7_1.mp4" width="9%" height="100%" loop></video>
		<video id="vid12" src="vid/Comp7_2.mp4" width="9%" height="100%" loop></video>
		<video id="vid13" src="vid/Comp7_3.mp4" width="9%" height="100%" loop></video>
		<video id="vid14" src="vid/Comp7_4.mp4" width="9%" height="100%" loop></video>
		<video id="vid15" src="vid/Comp7_5.mp4" width="9%" height="100%" loop></video>
		<video id="vid16" src="vid/Comp7_6.mp4" width="9%" height="100%" loop></video>
		<video id="vid17" src="vid/Comp7_7.mp4" width="9%" height="100%" loop></video>
		<video id="vid18" src="vid/Comp7_8.mp4" width="9%" height="100%" loop></video>
		<video id="vid19" src="vid/Comp7_9.mp4" width="9%" height="100%" loop></video>
		<video id="vid20" src="vid/Comp7_10.mp4" width="9%" height="100%" loop></video>
	</div>
	</body>
	<style>
		video {
		object-fit: fill;
		}
		#2 video{
			display:inline;
			position:relative;
			float:left;
		}
		#2{
			height:800px;
		}
	</style>
</html>

…and the JS.

//src:
//[1] https://stackoverflow.com/a/7790764 - capturing mouse pos
//[2] https://stackoverflow.com/a/10756409 - range conversion
//[3] https://stackoverflow.com/a/36731430 - FF/RW video to given time

function init() { //all js that needs to happen after page has loaded
    document.onmousemove = handleMouseMove; //[1]

    function handleMouseMove(event) {
        var dot, eventDoc, doc, body, pageX, pageY;

        event = event || window.event; //IE

        // If pageX/Y aren't available and clientX/Y are,
        // calculate pageX/Y - logic taken from jQuery.
        // (For old IE)
        if (event.pageX == null && event.clientX != null) {
            eventDoc = (event.target && event.target.ownerDocument) || document;
            doc = eventDoc.documentElement;
            body = eventDoc.body;

            event.pageX = event.clientX +
                (doc && doc.scrollLeft || body && body.scrollLeft || 0) -
                (doc && doc.clientLeft || body && body.clientLeft || 0);
            event.pageY = event.clientY +
                (doc && doc.scrollTop || body && body.scrollTop || 0) -
                (doc && doc.clientTop || body && body.clientTop || 0);
        }

        // console.log(event.pageX);
        var width, vid_length_s, vid_timeToSkipTo;
        // width = screen.width;
        width = window.innerWidth;
        vid_length = 25;
        vid_timeToSkipTo = convertToRange(event.pageX, [0, width], [0, vid_length]);
        console.log(vid_timeToSkipTo);
        goToTime(Math.floor(vid_timeToSkipTo));
    }
}

window.addEventListener('load', init);

function convertToRange(value, srcRange, dstRange) { //[2]
    // value is outside source range return
    if (value < srcRange[0] || value > srcRange[1]) {
        return NaN;
    }
    var srcMax = srcRange[1] - srcRange[0],
        dstMax = dstRange[1] - dstRange[0],
        adjValue = value - srcRange[0];

    return (adjValue * dstMax / srcMax) + dstRange[0];

}

function goToTime(time) { //[3]
    var vid11 = document.getElementById('vid11'),
        ticks11 = 10, // number of frames during fast-forward
        frms11 = 100, // number of milliseconds between frames in fast-forward/rewind
        endtime11 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta11 = (endtime11 - vid11.currentTime) / ticks11;
    var startTime11 = vid11.currentTime;
    for (let i = 0; i < ticks11; ++i) {
        (function(j) {
            setTimeout(function() {
                vid11.currentTime = startTime11 + tdelta11 * j;
            }, j * frms11);
        })(i);
    }
    var vid12 = document.getElementById('vid12'),
        ticks12 = 10, // number of frames during fast-forward
        frms12 = 150, // number of milliseconds between frames in fast-forward/rewind
        endtime12 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta12 = (endtime12 - vid12.currentTime) / ticks12;
    var startTime12 = vid12.currentTime;
    for (let i = 0; i < ticks12; ++i) {
        (function(j) {
            setTimeout(function() {
                vid12.currentTime = startTime12 + tdelta12 * j;
            }, j * frms12);
        })(i);
    }
    var vid13 = document.getElementById('vid13'),
        ticks13 = 10, // number of frames during fast-forward
        frms13 = 200, // number of milliseconds between frames in fast-forward/rewind
        endtime13 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta13 = (endtime13 - vid13.currentTime) / ticks13;
    var startTime13 = vid13.currentTime;
    for (let i = 0; i < ticks13; ++i) {
        (function(j) {
            setTimeout(function() {
                vid13.currentTime = startTime13 + tdelta13 * j;
            }, j * frms13);
        })(i);
    }
    var vid14 = document.getElementById('vid14'),
        ticks14 = 10, // number of frames during fast-forward
        frms14 = 250, // number of milliseconds between frames in fast-forward/rewind
        endtime14 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta14 = (endtime14 - vid14.currentTime) / ticks14;
    var startTime14 = vid14.currentTime;
    for (let i = 0; i < ticks14; ++i) {
        (function(j) {
            setTimeout(function() {
                vid14.currentTime = startTime14 + tdelta14 * j;
            }, j * frms14);
        })(i);
    }
    var vid15 = document.getElementById('vid15'),
        ticks15 = 10, // number of frames during fast-forward
        frms15 = 300, // number of milliseconds between frames in fast-forward/rewind
        endtime15 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta15 = (endtime15 - vid15.currentTime) / ticks15;
    var startTime15 = vid15.currentTime;
    for (let i = 0; i < ticks15; ++i) {
        (function(j) {
            setTimeout(function() {
                vid15.currentTime = startTime15 + tdelta15 * j;
            }, j * frms15);
        })(i);
    }
    var vid16 = document.getElementById('vid16'),
        ticks16 = 10, // number of frames during fast-forward
        frms16 = 350, // number of milliseconds between frames in fast-forward/rewind
        endtime16 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta16 = (endtime16 - vid16.currentTime) / ticks16;
    var startTime16 = vid16.currentTime;
    for (let i = 0; i < ticks16; ++i) {
        (function(j) {
            setTimeout(function() {
                vid16.currentTime = startTime16 + tdelta16 * j;
            }, j * frms16);
        })(i);
    }
    var vid17 = document.getElementById('vid17'),
        ticks17 = 10, // number of frames during fast-forward
        frms17 = 300, // number of milliseconds between frames in fast-forward/rewind
        endtime17 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta17 = (endtime17 - vid17.currentTime) / ticks17;
    var startTime17 = vid17.currentTime;
    for (let i = 0; i < ticks17; ++i) {
        (function(j) {
            setTimeout(function() {
                vid17.currentTime = startTime17 + tdelta17 * j;
            }, j * frms17);
        })(i);
    }
    var vid18 = document.getElementById('vid18'),
        ticks18 = 10, // number of frames during fast-forward
        frms18 = 250, // number of milliseconds between frames in fast-forward/rewind
        endtime18 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta18 = (endtime18 - vid18.currentTime) / ticks18;
    var startTime18 = vid18.currentTime;
    for (let i = 0; i < ticks18; ++i) {
        (function(j) {
            setTimeout(function() {
                vid18.currentTime = startTime18 + tdelta18 * j;
            }, j * frms18);
        })(i);
    }
    var vid19 = document.getElementById('vid19'),
        ticks19 = 10, // number of frames during fast-forward
        frms19 = 200, // number of milliseconds between frames in fast-forward/rewind
        endtime19 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta19 = (endtime19 - vid19.currentTime) / ticks19;
    var startTime19 = vid19.currentTime;
    for (let i = 0; i < ticks19; ++i) {
        (function(j) {
            setTimeout(function() {
                vid19.currentTime = startTime19 + tdelta19 * j;
            }, j * frms19);
        })(i);
    }
    var vid20 = document.getElementById('vid20'),
        ticks20 = 10, // number of frames during fast-forward
        frms20 = 150, // number of milliseconds between frames in fast-forward/rewind
        endtime20 = time; // time to fast-forward/rewind to (in seconds)
    // fast-forward/rewind video to end time 
    var tdelta20 = (endtime20 - vid20.currentTime) / ticks20;
    var startTime20 = vid20.currentTime;
    for (let i = 0; i < ticks20; ++i) {
        (function(j) {
            setTimeout(function() {
                vid20.currentTime = startTime20 + tdelta20 * j;
            }, j * frms20);
        })(i);
    }
}

The code is pretty sloppy and could probably be optimized a lot if given more time. The videos perform horribly on Firefox and Chrome, while working fine in Safari. In general, playing 10 videos at once obviously causes average-performing computers to increase in temperature at an unacceptable rate and would probably eventually cause the browser to crash. Instead, there is probably a way for one video file to be used that can be duplicated and cropped as necessary.

Leave a Reply

Your email address will not be published. Required fields are marked *