Music Theory Fundamentals

Project Summary

Music Theory Fundamentals was a school project I built to help music students learn music theory, rhythm, and ear training. I wrote all of the tutorials and provided corresponding quizzes to help them practice. Building my idea turned out to be a great way for me to learn JavaScript.

When I was a music major, I would have loved to have a tool like this to practice music theory between my classes. Because the target audience was college students, I needed the UI to be easy to use on mobile devices. I built the site to be responsive to accommodate that.

Music Theory is a subject most music majors dislike. (It's like having an artist take a math class.) I wanted to keep the design friendly and make sure the micro copy was encouraging.

What I Did

  • UI Design
  • Logo & Graphics
  • Wrote Content (tutorials, quizzes, etc.)
  • Front-end Build
  • Back-end Build

Technologies Used

  • Illustrator, Photoshop, MuseScore for graphics
  • HTML
  • CSS
  • JavaScript, jQuery
  • PHP
  • responsive site

UI Design

1440px:

Music Theory Fundamentals Tutorial 1440px wide Music Theory Fundamentals Quiz 1440px wide

510px:

Music Theory Fundamentals Tutorial 510px wide

Making the Quizzes Work

I knew I wanted the quizzes to work client-side because I wanted to provide immediate feedback to the user for each question. It was also a fun chance for me to figure out how to do this with JavaScript. If I decide to maintain this project, I'll probably add a server-side form that tracks the user's quiz results if logged in.

It works with HTML5 data attributes and JavaScript objects to store the quiz answer keys. (If I was building this now, I would have stored the data in a JSON file.) It connects the correct answer key with the corresponding quiz ID and answer ID. Here's the HTML:

<ol class="quiz" data–quiz–id="keySignaturesAnswerKey">
    <li class="group">
        <div class="question group">
            <p>What key signature is this?</p>
            <img src="../img/Quiz/keySignaturesQuiz/A.gif" alt="3 sharps" />
        </div><!–– end .question ––>
        <div class="choices">
            <form data–question–id="1">
                <label><input name="quizAnswer" type="radio" value="F" />F </label>
                <label><input name="quizAnswer" type="radio" value="A" />A </label>
                <label><input name="quizAnswer" type="radio" value="F sharp" />F sharp </label>
                <label><input name="quizAnswer" type="radio" value="C sharp" />C sharp </label>
                <a class="check–answer">check answer</a>
            </form>
        </div><!–– end .choices ––>
        <div class="answer">
            <p class="correct">Correct!</p>
            <p class="incorrect">Incorrect.</p>
        </div><!–– end .answer ––>
    </li>
    <li class="group">
        <div class="question group">
            <p>What key signature is this?</p>
            <img src="../img/Quiz/keySignaturesQuiz/C.gif" alt="no sharps or flats" />
        </div><!–– end .question ––>
        <div class="choices">
            <form data–question–id="2">
                <label><input name="quizAnswer" type="radio" value="A" />A </label>
                <label><input name="quizAnswer" type="radio" value="C" />C </label>
                <label><input name="quizAnswer" type="radio" value="F" />F </label>
                <label><input name="quizAnswer" type="radio" value="B" />B </label>
                <a class="check–answer">check answer</a>
            </form>
        </div><!–– end .choices ––>
        <div class="answer">
            <p class="correct">Correct!</p>
            <p class="incorrect">Incorrect.</p>
        </div><!–– end .answer ––>
    </li>

Here's the JavaScript that makes it work. I learned a lot about traversing the DOM with jQuery and abstraction in programming when writing this. (Abstraction was one of those "Ah Ha!" moments for me.)

//Answer Keys
var answerKeys = {
    //other answer keys here ...

    //Key Signatures Quiz
    keySignaturesAnswerKey: {
        1: 'A',
        2: 'C',
        3: 'G',
        4: 'E flat',
        5: 'A flat',
        6: 'F',
        7: 'C sharp',
        8: 'E flat',
        9: 'B',
        10: 'D',
        11: 'B flat',
        12: 'G flat',
        13: '6',
        14: 'E',
        15: 'D flat',
        16: 'C flat',
        17: 'none',
        18: 'F sharp',
        19: 'A flat',
        20: '1'
    }
};

//when check answer, provides response
$('.check–answer').click(function(){
    var answers = $(this).parents('.choices');
    var getResult = answers.siblings('.answer');
    var question = answers.find('form').attr('data–question–id');
    var answer = answers.find('input:checked').val();
    var quiz = $(this).parents('ol.quiz').attr('data–quiz–id');
    if(answerKeys[quiz][question] == answer){
                    getResult.find('.correct').fadeIn(500);
                    getResult.find('.incorrect').hide();
    } else {
                    getResult.find('.correct').hide();
                    getResult.find('.incorrect').fadeIn(500);
    }
});

Front-end Build

I tried to use as few images as possible for styling and, instead, use CSS. In fact, the only image used for styling is the background image of the header. The arrows for the navigation, quizzes, and form validation, for example, were created with CSS only.

quiz check answer response styling form validation styling
/***** Global Nav Arrows *****/
.mainSlideUp:after {
    content: "\00A0";
    width: 0;
    height: 0;
    border–width: 8px 0 8px 15px;
    border–style: solid;
    border–color: transparent transparent transparent #CCC100;
    position: absolute;
    left: –20px;
    top: 0;
    margin–top: 0.37em;
}
.subSlideUp li:after {
    content: "\00A0";
    width: 0;
    height: 0;
    border–width: 5px 0 5px 10px;
    border–style: solid;
    border–color: transparent transparent transparent #CCC100;
    position: absolute;
    left: –15px;
    top: 0;
    margin–top: 0.37em;
}

/***** Quiz Arrows *****/
.correct {
    background–color: #CCC100;
    text–shadow: –2px 1px 1px rgba(235, 228, 118, .7);
    padding: 0.5em 0;
}
.incorrect {
    background–color: #807B6E;
    color: #FCFCF9;
    text–shadow: 1px 0 1px rgba(0, 0, 0, .9);
    padding: 0.5em 0;
}
.correct:after, .incorrect:after {
    content: "\00A0";
    width: 0;
    height: 0;
    border–width: 1.15em 15px 1.15em 0;
    border–style: solid;
    position: absolute;
    top: 0px;
    left: –15px;
}
.correct:after {
    border–color: transparent #CCC100 transparent transparent;
}
.incorrect:after {
    border–color: transparent #807B6E transparent transparent;
}

/***** Feedback Form Validation Arrows *****/
label.error {
    background–color: #CCC100;
    position: absolute;
    padding: 0.15em 2.1067415730337078651685393258427%; /* 15/712 */
    left: 38%; /* from #feedbackContent, same width as input[type="text"] */
}
label.error:after { /* left arrow of error messages */
    content: "\00A0";
    width: 0;
    height: 0;
    border–width: 0.75em 12px 0.75em 0;
    border–style: solid;
    border–color: transparent #CCC100 transparent transparent;
    position: absolute;
    top: 0px;
    left: –12px;
}
input.error {
    border: 1px solid #CCC100;
}

Back-end Build

Because this is potentially a very large site, I separated the content into PHP includes to keep things simple.

<?php include('includes/header.php'); ?>
    <div id="mainContent">
        <!–– main content here ––>
    </div>
<?php include('includes/sidebar.php'); ?>
<?php include('includes/footer.php'); ?>

I also used PHP for the feedback form and used JavaScript to show/hide questions based on the user's answers.