Friday, January 13, 2006

Sleep Hack

Ok, for now, I'm just going to take a departure from my normal programming-intensive topics to go with a sleep hack. What is a sleep hack you say? Well, this one got dugg recently: Polyphasic Sleep. The short of it is that Steve Pavlina managed to condition his body to sleep for only three total hours a day by only taking short naps of around 25 minutes apiece many times throughout the day. He reports vast productivity gains with no particular downside. Evidently this comes by forcing the body to fall asleep faster and head straight down into REM sleeping almost as soon as your eyes close. Fascinating.

I've always slept for about 9 hours a day bcause I can't seem to get by on less, and I've always thought that I really had to be especially productive during the remaining 15 hours because I had so much less time to work with than my 6-7 hour a night sleeping friends. I've tried working out more and going with less sleep since it has been reported that physically active people sleep less, but I haven't found that to be true for me. However, if we really can train our bodies to drop sleep stages faster by forcing it into smaller naps, that would be great news. Unfortunately, I am a college student and I have classes to attend to and my schedule just won't let me drop off any ol' time to take a nap, so I'm going to try a different experiment, let's call it SplitNight napping.

SplitNight napping appropriately follows my own personal naming convention for classes in Java or C++, but that's completely irrelavent. More relevant is that I'm going to try splitting my night apart into a sleep phase ending at midnight and another ending at 7:00am. In between, I intend to stay up for at least 3 hours every night. If, mathematically speaking, napping once a night leads to 9 hours of sleep per day and napping 6 times a night leads to 3 hours of sleep per day, I figure that this should bring me down to somewhere around 6 hours of sleep total.

Hopefully, this less drastic plan will also allow me to actually be a functional human being during the "transition" phase which is important as my volleyball team plays Sunday and school starts the following day. :)

Day 1:
Tried to force myself to sleep before midnight. I'm not consciously aware of it happening, but I did spend a little over an hour in bed before rising from sheer boredom. In addition to helping me rest a little bit, it gave me time to think of such things as: how much extra time will I be spending changing into and out of pajamas on this new schedule?
Sleep total: 5 hours, Mental AcuityEstimate: 75%

Day 2:
I don't know if I really exactly stuck to the true intent of the experiment today. I appropriately woke up at midnight and then 7:00 as I was supposed to, but then I let myself go back to sleep again for another 3 hours session. My theory being at the time that as long as I was forcing myself into 3 hour sleep intervals, I was helping my body adjust to the new schedule. In any case, I slep another 3 hours until 10:00 and it felt great :)
Sleep Total: 7 hours, Mental Acuity Estimate: 90%

Day 3:
Today, I really tried to hold strictly to the regimen, but my biorhythms sure weren't cooperating. I only managed to get an hour in before midnight and another hour or two in before 7:00 am. A little afternoon, I fell asleep again for a couple of hourse nearly missing class. So, the good was that today I've only had my goal 6 hours of sleep, but my body is so not used to it. I could barely function earlier this morning, having to reread my homework assignments over and over to even remember what any given paragraph had just said. Can only hope this gets better tomorrow.
Sleep Total: 6 hours, Mental Acuity Estimate: 40%

Day 4:
I woke up exhausted again after a total of four hours of sleep. I have found it almost impossible to actually get to sleep for the second half of my nightly sleep. I slept for two hours, woke up at midnight like I was supposed to, and then slept for... not at all. Again. I believe the issue here is that the little 2 hour sleep invigorates me too darned much. It gives me enough energy for many hours of being awake, but my schedule isn't supporting that. So, I do believe I am going to change the experiment. I am going to try to push my two naps as far away from each other as possible so that each one only needs to energize me for the following 9 hours or so.
Sleep Total: 2 hours, Mental Acuity Estimate 75%

Day 5:
Well, I thought this after the Day 1 and Day 2 of the original experiment, but I really think that splitting my sleep schedule into two equidistant naps is going to work. Today, I woke up a bit groggy but quite competent. I could program, I went to class and payed pretty good attention, and then about 12 hours later, I was just ready for my nap. I didn't have to force myself down and I didn't feel too bad when I got up later. Part of this is probably psychological. When you sleep the whole night at once, you know its going to be another 18 hours or so before you have a chance to sleep again. Now that I'm splitting my night, I only have to wait 9 hours until I get another chance at sleep.
Sleep Total: 6 hours, Mental Acuity Estimate 85%

Day 7:

So it has been a week, and this is my sixth report,
which evidently
means that for one or more of the days I was absurdly sleepy and lost
all track of time. Especially at the beginning of this experiment, I
did notice that my sense of what day it was had drastically diminished.
I think I'm getting used to it now though.
In any case, yesterday I completely missed my afternoon nap and then
when night snowboarding on Cypress
- and had an absolute blast without really feeling
tired. So, evidently, its a bit easier to lose sleep if you know you
have another nap time coming soon.
Sleep Total: 4 hours, Mental Acuity Estimate 100%

Day 8:
Well, between the snowboarding and the lack of sleep I slept for four hours+ during each of my nap sessions. I wasn't completely functional during my first daytime, but did pretty well during my second daytime. I really do try to simulate daytime during these uptimes and I feel that may be why it has been easier and easier to wake up as my body's natural biorhythms adjust to the new schedule. As soon as I wake up, the first thing I do is turn on a light and leave it on, and then make sure I keep whatever room I'm in well lit as i go.

In any case, I am starting to think that this experiment is just plain working and as such is growing less interesting with each passing day, so I'm gonna tone it down and try posting weekly updates for a couple of weeks and then (hopefully) just call it a success. :)
Sleep Total: 8 hours, Mental Acuity Estimate 80%

Week 2:
This week was a mitigated failure worthy of a thumbs down icon if I were inspired enough to go find one. The biggest issue was that life kept getting in the way of my 3-6 PM nap. The second biggest issue was the snooze button. Between the two of them I got worse and worse throughout the week until I ended up with a single 9 hour long sleep on Friday. As of Saturday, though, I am back on schedule and hoping for better progress next week.
Sleep Average: 8 hours, Mental Acuity Estimate: 95%

Week 3:
Went pretty well. Two interesting notes: You are way more likely to get woken up by random environmental noise at 3-6PM than 3-6AM, and second, possibly because of the first, I always have a LOT more trouble waking up at 6AM. I will often just roll out of bed before the alarm hits at 6PM wide awake and ready to get back to work. 6AM is still very hard for me though.
Sleep Average: 8 hours, Mental Acuity Estimate: 100%

Week 4 (The Last week):
Success! The only real drawback at present is that it can be difficult to schedule in that daytime nap. Besides that, I sleep 7 to 8 hours a day on average, which though not quite the benefits I was hoping for, is still a worthwhile gain. On occasion, I have thought about giving up this experiment, and mostly this was because of the difficulty of finding a quiet place from around 3-6PM. In the end though, I do not currently think it is worth the loss in productivity to go back to a long night's sleep.

I hope these results have helped you, and as you can see it is possible to gain a significant number of hours by splitting your night up without going to the drastic extremes of polyphasic sleep. As always, though, I'm sure mileage may vary, and if you've had some interesting experience with these please leave me a comment and let me know. :)

Thursday, January 05, 2006

iTunes Style Music DB with AJAX

I recently decided that since I have some free time on my hands, something that would look really cool is a web page with a music database that works kinda like the iTunes window. The pretty tables are all good, but what I really love about the iTunes window is the Search box. You type things in and then the results appear instantly below. With AJAX, we can do the same thing...

First off, since we all like playing with the end results first, you can find one version of the results here. Do note that that Firefox has some minor issues with this page and Opera some more major ones. More on that later.

Next, we need just a little bit of background. Before starting this project, the site had a mySQL database table called "choir". The choir table holds such information as the song title, what time it was recorded, and the actual location of the audio file.

I decided to do this in PHP, so, the first thing I needed was a function to print the table where the search results would be displayed. This function needs to take two parameters: one parameter indicates which text the user is searching for (starts blank) and the second parameter indicates which entry the results should start at (starts at zero). This means that we only ever need one PHP function to print the data.

One last thing before we look at the main function. I used a very small helper function for outputting cells of a table which looks like so:

function cellPrint($cellText)
echo "<td>$cellText</td>";

All of this information is contained in a file called choirwindow.php.

Step one then is to connect to the database and query for the information.

function printChoirs($searchText,$startNumber)
//This variable represents the maximum number of songs to be shown
//on one page.
$maxAudioTableSize = 20;

//Keep the username/password/db stuff in easy to grep for places so they
//are easy to change if you need to later.
$username = "SuperUser";
$password = "AdminPassword";
$database = "SuperSpecialDatabase";

//Connect to the mySQL database
$link = mysql_connect(localhost,$username,$password);
@mysql_select_db($database) or die (mysql_error());

//This first query is to find out how many choir songs total match up to the
//search text. For optimization, this could be buffered and passed along in
//a post variable if the user is going to spend a lot of time selecting the NEXT
//or PREVIOUS links.
$countQuery = "SELECT count(*) from choir";
$countQuery = $countQuery . " WHERE song_name LIKE '%$searchText%'";
$countResult = mysql_query($countQuery);
$count = mysql_result($countResult,0,"count(*)");

//This second query is to retrieve the actual song information that we are
//going to display and save it in the $result variable.
$infoquery = "SELECT * FROM choir";
$infoquery = $infoquery . " WHERE song_name LIKE '%$searchText%'";
$infoquery = $infoquery . " order by date DESC";
$infoquery = $infoquery . " limit $startNumber,$maxAudioTableSize";
$result = mysql_query($infoquery);

//Let the user know how many search results he/she has.
echo "There are: " . $count . " choir song(s)";

One interesting thing to note from this is that mySQL is doing all of the work for the search mechanism through the use of the LIKE construct. This is not only lazy but good design as well as the search algorithm for mySQL is quite fast and running natively as compared to any possible solution in PHP.

Next up, we need to output the table itself. This is done quite simply by echoing out each row of the mySQL $result to a row of an HTML table. In the code, you will note that the table is completely unadorned. The visual look of the table is cleaned up later using css.

//$num is the number of results we have for THIS page.
$num = mysql_numrows($result);

//Start the HTML table.
echo "<table>";

//Output Table Header.
echo <thead><tr>";
cellPrint("Song Name");
echo "</tr></thead>";

//Now the table body.
echo "<tbody>";
$i = 0;
while ($i < $num)
echo "<tr>";
$dateString = strftime("%m/%d/%y",$date);
cellPrint("<a href=\"$filename\">Download</a>");
echo "</tr>";

echo "</tbody>";

echo "</table>";

//Don't forget to close your db connection!

Now that the table itself is on the page, the only other HTML we have to put out is the code for the "hyperlinks" at the bottom of the table that allow navigation through various pages of results. You may have noticed from the web page that these are not actually hyperlinks, they just look like them. They are actually connected to a javascript function called "reloadMusic" which we will get to shortly. They appear to be real links because they are set to the class "fakelink" which corresponds to the following css rule.


Now, the code:

//We only need to do this if there are more total entries than current...
//Remember, $count is total entries matching the search string whereas
//$num is the number of entries returned by our mySQL query.
if ($count > $num)

// Print out PREV if necessary
if ($startNumber > 0)
$previousNumber = $startNumber-$maxAudioTableSize;
echo "<a class=\"fakelink\" onclick=\"reloadMusic('$searchText',$i);\">$page</a> ";

// Print out a bunch of numbers for each of the pages.
$i = 0; //This will be the base index number for each page set.
$page = 1; //Which "Page" is this located... the 1,2,3,4 selection.
while ($i < $count)
if ($i <> $startNumber)
echo " <a class=\"fakelink\" onclick=\"reloadMusic('$searchText',$i);\">$page</a> ";
echo " $page ";

$i = $i+$maxAudioTableSize;

// Print out NEXT if necessary.
if ( ($startNumber+$maxAudioTableSize) < $count)
$nextNumber = $startNumber+$maxAudioTableSize;
echo "<a class=\"fakelink\" onclick=\"reloadMusic('$searchText',$nextNumber);\">Next</a>";


This brings us naturally to the question of the javascript. Obviously, if I'm calling this AJAX (Asynchronous Javascript and XML) it's gotta have some javascript in it. Although, I confess, I am not making use of XML in this example which technically makes it AJAH (Asynchronous Javascript and HTML) but that is a much more obtuse term. In any case, it is time to examine the Javascript. The javascript is printed out in a seperate PHP function which is called by parent file shortly after (or in) its header. The javascript could easily have stood on its own in this case, but I wanted to make the file as modular as possible. Hence, the PHP function that prints out javascript:

function printChoirHeader()
<script language=\"javascript\" type=\"text/javascript\" src=\"./ajax.js\"></script>
<script language=\"javascript\" type=\"text/javascript\">

function reloadMusic(searchText,startNumber){
var url =\"choirwindow.php?searchText=\" + escape(searchText);
url = url + \"&startNumber=\" + escape(startNumber);\"GET\", url,true);
http.onreadystatechange = handleHTTPResponse;

function search(){
var searchText = document.getElementById(\"searchtext\").value;
var url = \"choirwindow.php?searchText=\" + escape(searchText);
url = url + \"&startNumber=0\";\"GET\", url,true);
http.onreadystatechange = handleHTTPResponse;

function handleHTTPResponse() {
if (http.readyState == 4) {
document.getElementById('audiotable').innerHTML = http.responseText;

var http = getHTTPObject(); // We create the HTTP Object

This uses the same pattern as my appearing map example, so it might be helpful to get some review there. Basically, the reloadMusic() and search() functions take whatever parameters were previously there and ferry them on to a call to choirwindow.php and pass GET parameters for the search text and the startNumber which is the mySQL index of what will be the first entry of the new table. The reload music function is passed these parameters by the PHP code that we have previously seen and does no work for them on its own. When writing the search function, it is already known that since we are now performing a new search, we will start at a new index (0). All the javascript function has to do then is to get the search text which is does by finding the box with the id of "searchtext" in the calling page. This naturally means that any file that makes use of the choirwindow.php functionality and desires to use its search functionality must give its searchbox an id of "searchtext". (See Requirements section below). Similarly, the calling file must have a division whose id is "audiotable" in which to print this table by calling printChoirs("",0);

Now, this code calls back to choirwindow.php with its GET parameters. These are handled outside of any function by the following code:

$searchText = $_GET['searchText'];
$startNumber = $_GET['startNumber'];

if (isset($searchText))

This means that when the javascript calls choirwindow.php with parameters, choirwindow.php will respond by printing its music table. However, when the file is included, since these parameters will not be set during the include call, no output will muddy up the top of the page.

Finally, for the sake of thoroughness, let's see the code with the search box and audiotable div:

Search: <input maxlength="60" size="60" name="searchtext" id="searchtext" onkeyup="search();">

<div id="audiotable" class="audiotable">


In order to use this PHP functionality then, the calling PHP file must do the following:
  1. Must contain a search field named "searchtext" whose onClick function calls "search();"
  2. Must call printChoirHeader() in its header
  3. Must call printChoirs() from within a div whose id is "audiotable"

Firefox and Opera issues:
Both Firefox and Opera have, on occasion, issues with this functionality. While both have different visual manifestations, they seem to have the same root cause. After the asynchronous call to reload the page, Firefox sometimes throws a javascript exception and refuses to redraw the page. The result of this is that the table dissapears. Interestingly, if you view the source that Firefox sees, you will find that the source is rendered perfectly. Additionally, you can save the source as an html file, reload with Firefox, and everything will appear correctly. Not being a Firefox developer, I cannot tell you why exactly that is, but, I can point you to the bug report where you can vote for it to get fixed faster :)

Similarly, Opera sometimes "forgets" to erase what was in the audiotable div before, resulting in two different musical tables in the results box. Like with the Firefox bug, though, this seems to be a rendering issue as you can examine the source that Opera is trying to render and the source is correct.

Internet Explorer seems to render the table perfectly every time.