diff --git a/data/CIE_XYZ.csv b/data/CIE_XYZ.csv
new file mode 100644
--- /dev/null
+++ b/data/CIE_XYZ.csv
@@ -0,0 +1,441 @@
+390,3.769647E-03,4.146161E-04,1.847260E-02
+391,4.532416E-03,5.028333E-04,2.221101E-02
+392,5.446553E-03,6.084991E-04,2.669819E-02
+393,6.538868E-03,7.344436E-04,3.206937E-02
+394,7.839699E-03,8.837389E-04,3.847832E-02
+395,9.382967E-03,1.059646E-03,4.609784E-02
+396,1.120608E-02,1.265532E-03,5.511953E-02
+397,1.334965E-02,1.504753E-03,6.575257E-02
+398,1.585690E-02,1.780493E-03,7.822113E-02
+399,1.877286E-02,2.095572E-03,9.276013E-02
+400,2.214302E-02,2.452194E-03,1.096090E-01
+401,2.601285E-02,2.852216E-03,1.290077E-01
+402,3.043036E-02,3.299115E-03,1.512047E-01
+403,3.544325E-02,3.797466E-03,1.764441E-01
+404,4.109640E-02,4.352768E-03,2.049517E-01
+405,4.742986E-02,4.971717E-03,2.369246E-01
+406,5.447394E-02,5.661014E-03,2.725123E-01
+407,6.223612E-02,6.421615E-03,3.117820E-01
+408,7.070048E-02,7.250312E-03,3.547064E-01
+409,7.982513E-02,8.140173E-03,4.011473E-01
+410,8.953803E-02,9.079860E-03,4.508369E-01
+411,9.974848E-02,1.005608E-02,5.034164E-01
+412,1.104019E-01,1.106456E-02,5.586361E-01
+413,1.214566E-01,1.210522E-02,6.162734E-01
+414,1.328741E-01,1.318014E-02,6.760982E-01
+415,1.446214E-01,1.429377E-02,7.378822E-01
+416,1.566468E-01,1.545004E-02,8.013019E-01
+417,1.687901E-01,1.664093E-02,8.655573E-01
+418,1.808328E-01,1.785302E-02,9.295791E-01
+419,1.925216E-01,1.907018E-02,9.921293E-01
+420,2.035729E-01,2.027369E-02,1.051821E+00
+421,2.137531E-01,2.144805E-02,1.107509E+00
+422,2.231348E-01,2.260041E-02,1.159527E+00
+423,2.319245E-01,2.374789E-02,1.208869E+00
+424,2.403892E-01,2.491247E-02,1.256834E+00
+425,2.488523E-01,2.612106E-02,1.305008E+00
+426,2.575896E-01,2.739923E-02,1.354758E+00
+427,2.664991E-01,2.874993E-02,1.405594E+00
+428,2.753532E-01,3.016909E-02,1.456414E+00
+429,2.838921E-01,3.165145E-02,1.505960E+00
+430,2.918246E-01,3.319038E-02,1.552826E+00
+431,2.989200E-01,3.477912E-02,1.595902E+00
+432,3.052993E-01,3.641495E-02,1.635768E+00
+433,3.112031E-01,3.809569E-02,1.673573E+00
+434,3.169047E-01,3.981843E-02,1.710604E+00
+435,3.227087E-01,4.157940E-02,1.748280E+00
+436,3.288194E-01,4.337098E-02,1.787504E+00
+437,3.349242E-01,4.517180E-02,1.826609E+00
+438,3.405452E-01,4.695420E-02,1.863108E+00
+439,3.451688E-01,4.868718E-02,1.894332E+00
+440,3.482554E-01,5.033657E-02,1.917479E+00
+441,3.494153E-01,5.187611E-02,1.930529E+00
+442,3.489075E-01,5.332218E-02,1.934819E+00
+443,3.471746E-01,5.470603E-02,1.932650E+00
+444,3.446705E-01,5.606335E-02,1.926395E+00
+445,3.418483E-01,5.743393E-02,1.918437E+00
+446,3.390240E-01,5.885107E-02,1.910430E+00
+447,3.359926E-01,6.030809E-02,1.901224E+00
+448,3.324276E-01,6.178644E-02,1.889000E+00
+449,3.280157E-01,6.326570E-02,1.871996E+00
+450,3.224637E-01,6.472352E-02,1.848545E+00
+451,3.156225E-01,6.614749E-02,1.817792E+00
+452,3.078201E-01,6.757256E-02,1.781627E+00
+453,2.994771E-01,6.904928E-02,1.742514E+00
+454,2.909776E-01,7.063280E-02,1.702749E+00
+455,2.826646E-01,7.238339E-02,1.664439E+00
+456,2.747962E-01,7.435960E-02,1.629207E+00
+457,2.674312E-01,7.659383E-02,1.597360E+00
+458,2.605847E-01,7.911436E-02,1.568896E+00
+459,2.542749E-01,8.195345E-02,1.543823E+00
+460,2.485254E-01,8.514816E-02,1.522157E+00
+461,2.433039E-01,8.872657E-02,1.503611E+00
+462,2.383414E-01,9.266008E-02,1.486673E+00
+463,2.333253E-01,9.689723E-02,1.469595E+00
+464,2.279619E-01,1.013746E-01,1.450709E+00
+465,2.219781E-01,1.060145E-01,1.428440E+00
+466,2.151735E-01,1.107377E-01,1.401587E+00
+467,2.075619E-01,1.155111E-01,1.370094E+00
+468,1.992183E-01,1.203122E-01,1.334220E+00
+469,1.902290E-01,1.251161E-01,1.294275E+00
+470,1.806905E-01,1.298957E-01,1.250610E+00
+471,1.707154E-01,1.346299E-01,1.203696E+00
+472,1.604471E-01,1.393309E-01,1.154316E+00
+473,1.500244E-01,1.440235E-01,1.103284E+00
+474,1.395705E-01,1.487372E-01,1.051347E+00
+475,1.291920E-01,1.535066E-01,9.991789E-01
+476,1.189859E-01,1.583644E-01,9.473958E-01
+477,1.090615E-01,1.633199E-01,8.966222E-01
+478,9.951424E-02,1.683761E-01,8.473981E-01
+479,9.041850E-02,1.735365E-01,8.001576E-01
+480,8.182895E-02,1.788048E-01,7.552379E-01
+481,7.376817E-02,1.841819E-01,7.127879E-01
+482,6.619477E-02,1.896559E-01,6.725198E-01
+483,5.906380E-02,1.952101E-01,6.340976E-01
+484,5.234242E-02,2.008259E-01,5.972433E-01
+485,4.600865E-02,2.064828E-01,5.617313E-01
+486,4.006154E-02,2.121826E-01,5.274921E-01
+487,3.454373E-02,2.180279E-01,4.948809E-01
+488,2.949091E-02,2.241586E-01,4.642586E-01
+489,2.492140E-02,2.307302E-01,4.358841E-01
+490,2.083981E-02,2.379160E-01,4.099313E-01
+491,1.723591E-02,2.458706E-01,3.864261E-01
+492,1.407924E-02,2.546023E-01,3.650566E-01
+493,1.134516E-02,2.640760E-01,3.454812E-01
+494,9.019658E-03,2.742490E-01,3.274095E-01
+495,7.097731E-03,2.850680E-01,3.105939E-01
+496,5.571145E-03,2.964837E-01,2.948102E-01
+497,4.394566E-03,3.085010E-01,2.798194E-01
+498,3.516303E-03,3.211393E-01,2.654100E-01
+499,2.887638E-03,3.344175E-01,2.514084E-01
+500,2.461588E-03,3.483536E-01,2.376753E-01
+501,2.206348E-03,3.629601E-01,2.241211E-01
+502,2.149559E-03,3.782275E-01,2.107484E-01
+503,2.337091E-03,3.941359E-01,1.975839E-01
+504,2.818931E-03,4.106582E-01,1.846574E-01
+505,3.649178E-03,4.277595E-01,1.720018E-01
+506,4.891359E-03,4.453993E-01,1.596918E-01
+507,6.629364E-03,4.635396E-01,1.479415E-01
+508,8.942902E-03,4.821376E-01,1.369428E-01
+509,1.190224E-02,5.011430E-01,1.268279E-01
+510,1.556989E-02,5.204972E-01,1.176796E-01
+511,1.997668E-02,5.401387E-01,1.094970E-01
+512,2.504698E-02,5.600208E-01,1.020943E-01
+513,3.067530E-02,5.800972E-01,9.527993E-02
+514,3.674999E-02,6.003172E-01,8.890075E-02
+515,4.315171E-02,6.206256E-01,8.283548E-02
+516,4.978584E-02,6.409398E-01,7.700982E-02
+517,5.668554E-02,6.610772E-01,7.144001E-02
+518,6.391651E-02,6.808134E-01,6.615436E-02
+519,7.154352E-02,6.999044E-01,6.117199E-02
+520,7.962917E-02,7.180890E-01,5.650407E-02
+521,8.821473E-02,7.351593E-01,5.215121E-02
+522,9.726978E-02,7.511821E-01,4.809566E-02
+523,1.067504E-01,7.663143E-01,4.431720E-02
+524,1.166192E-01,7.807352E-01,4.079734E-02
+525,1.268468E-01,7.946448E-01,3.751912E-02
+526,1.374060E-01,8.082074E-01,3.446846E-02
+527,1.482471E-01,8.213817E-01,3.163764E-02
+528,1.593076E-01,8.340701E-01,2.901901E-02
+529,1.705181E-01,8.461711E-01,2.660364E-02
+530,1.818026E-01,8.575799E-01,2.438164E-02
+531,1.931090E-01,8.682408E-01,2.234097E-02
+532,2.045085E-01,8.783061E-01,2.046415E-02
+533,2.161166E-01,8.879907E-01,1.873456E-02
+534,2.280650E-01,8.975211E-01,1.713788E-02
+535,2.405015E-01,9.071347E-01,1.566174E-02
+536,2.535441E-01,9.169947E-01,1.429644E-02
+537,2.671300E-01,9.269295E-01,1.303702E-02
+538,2.811351E-01,9.366731E-01,1.187897E-02
+539,2.954164E-01,9.459482E-01,1.081725E-02
+540,3.098117E-01,9.544675E-01,9.846470E-03
+541,3.241678E-01,9.619834E-01,8.960687E-03
+542,3.384319E-01,9.684390E-01,8.152811E-03
+543,3.525786E-01,9.738289E-01,7.416025E-03
+544,3.665839E-01,9.781519E-01,6.744115E-03
+545,3.804244E-01,9.814106E-01,6.131421E-03
+546,3.940988E-01,9.836669E-01,5.572778E-03
+547,4.076972E-01,9.852081E-01,5.063463E-03
+548,4.213484E-01,9.863813E-01,4.599169E-03
+549,4.352003E-01,9.875357E-01,4.175971E-03
+550,4.494206E-01,9.890228E-01,3.790291E-03
+551,4.641616E-01,9.910811E-01,3.438952E-03
+552,4.794395E-01,9.934913E-01,3.119341E-03
+553,4.952180E-01,9.959172E-01,2.829038E-03
+554,5.114395E-01,9.980205E-01,2.565722E-03
+555,5.280233E-01,9.994608E-01,2.327186E-03
+556,5.448696E-01,9.999930E-01,2.111280E-03
+557,5.618898E-01,9.997557E-01,1.915766E-03
+558,5.790137E-01,9.989839E-01,1.738589E-03
+559,5.961882E-01,9.979123E-01,1.577920E-03
+560,6.133784E-01,9.967737E-01,1.432128E-03
+561,6.305897E-01,9.957356E-01,1.299781E-03
+562,6.479223E-01,9.947115E-01,1.179667E-03
+563,6.654866E-01,9.935534E-01,1.070694E-03
+564,6.833782E-01,9.921156E-01,9.718623E-04
+565,7.016774E-01,9.902549E-01,8.822531E-04
+566,7.204110E-01,9.878596E-01,8.010231E-04
+567,7.394495E-01,9.849324E-01,7.273884E-04
+568,7.586285E-01,9.815036E-01,6.606347E-04
+569,7.777885E-01,9.776035E-01,6.001146E-04
+570,7.967750E-01,9.732611E-01,5.452416E-04
+571,8.154530E-01,9.684764E-01,4.954847E-04
+572,8.337389E-01,9.631369E-01,4.503642E-04
+573,8.515493E-01,9.571062E-01,4.094455E-04
+574,8.687862E-01,9.502540E-01,3.723345E-04
+575,8.853376E-01,9.424569E-01,3.386739E-04
+576,9.011588E-01,9.336897E-01,3.081396E-04
+577,9.165278E-01,9.242893E-01,2.804370E-04
+578,9.318245E-01,9.146707E-01,2.552996E-04
+579,9.474524E-01,9.052333E-01,2.324859E-04
+580,9.638388E-01,8.963613E-01,2.117772E-04
+581,9.812596E-01,8.883069E-01,1.929758E-04
+582,9.992953E-01,8.808462E-01,1.759024E-04
+583,1.017343E+00,8.736445E-01,1.603947E-04
+584,1.034790E+00,8.663755E-01,1.463059E-04
+585,1.051011E+00,8.587203E-01,1.335031E-04
+586,1.065522E+00,8.504295E-01,1.218660E-04
+587,1.078421E+00,8.415047E-01,1.112857E-04
+588,1.089944E+00,8.320109E-01,1.016634E-04
+589,1.100320E+00,8.220154E-01,9.291003E-05
+590,1.109767E+00,8.115868E-01,8.494468E-05
+591,1.118438E+00,8.007874E-01,7.769425E-05
+592,1.126266E+00,7.896515E-01,7.109247E-05
+593,1.133138E+00,7.782053E-01,6.507936E-05
+594,1.138952E+00,7.664733E-01,5.960061E-05
+595,1.143620E+00,7.544785E-01,5.460706E-05
+596,1.147095E+00,7.422473E-01,5.005417E-05
+597,1.149464E+00,7.298229E-01,4.590157E-05
+598,1.150838E+00,7.172525E-01,4.211268E-05
+599,1.151326E+00,7.045818E-01,3.865437E-05
+600,1.151033E+00,6.918553E-01,3.549661E-05
+601,1.150002E+00,6.791009E-01,3.261220E-05
+602,1.148061E+00,6.662846E-01,2.997643E-05
+603,1.144998E+00,6.533595E-01,2.756693E-05
+604,1.140622E+00,6.402807E-01,2.536339E-05
+605,1.134757E+00,6.270066E-01,2.334738E-05
+606,1.127298E+00,6.135148E-01,2.150221E-05
+607,1.118342E+00,5.998494E-01,1.981268E-05
+608,1.108033E+00,5.860682E-01,1.826500E-05
+609,1.096515E+00,5.722261E-01,1.684667E-05
+610,1.083928E+00,5.583746E-01,1.554631E-05
+611,1.070387E+00,5.445535E-01,1.435360E-05
+612,1.055934E+00,5.307673E-01,1.325915E-05
+613,1.040592E+00,5.170130E-01,1.225443E-05
+614,1.024385E+00,5.032889E-01,1.133169E-05
+615,1.007344E+00,4.895950E-01,1.048387E-05
+616,9.895268E-01,4.759442E-01,0.000000E+00
+617,9.711213E-01,4.623958E-01,0.000000E+00
+618,9.523257E-01,4.490154E-01,0.000000E+00
+619,9.333248E-01,4.358622E-01,0.000000E+00
+620,9.142877E-01,4.229897E-01,0.000000E+00
+621,8.952798E-01,4.104152E-01,0.000000E+00
+622,8.760157E-01,3.980356E-01,0.000000E+00
+623,8.561607E-01,3.857300E-01,0.000000E+00
+624,8.354235E-01,3.733907E-01,0.000000E+00
+625,8.135565E-01,3.609245E-01,0.000000E+00
+626,7.904565E-01,3.482860E-01,0.000000E+00
+627,7.664364E-01,3.355702E-01,0.000000E+00
+628,7.418777E-01,3.228963E-01,0.000000E+00
+629,7.171219E-01,3.103704E-01,0.000000E+00
+630,6.924717E-01,2.980865E-01,0.000000E+00
+631,6.681600E-01,2.861160E-01,0.000000E+00
+632,6.442697E-01,2.744822E-01,0.000000E+00
+633,6.208450E-01,2.631953E-01,0.000000E+00
+634,5.979243E-01,2.522628E-01,0.000000E+00
+635,5.755410E-01,2.416902E-01,0.000000E+00
+636,5.537296E-01,2.314809E-01,0.000000E+00
+637,5.325412E-01,2.216378E-01,0.000000E+00
+638,5.120218E-01,2.121622E-01,0.000000E+00
+639,4.922070E-01,2.030542E-01,0.000000E+00
+640,4.731224E-01,1.943124E-01,0.000000E+00
+641,4.547417E-01,1.859227E-01,0.000000E+00
+642,4.368719E-01,1.778274E-01,0.000000E+00
+643,4.193121E-01,1.699654E-01,0.000000E+00
+644,4.018980E-01,1.622841E-01,0.000000E+00
+645,3.844986E-01,1.547397E-01,0.000000E+00
+646,3.670592E-01,1.473081E-01,0.000000E+00
+647,3.497167E-01,1.400169E-01,0.000000E+00
+648,3.326305E-01,1.329013E-01,0.000000E+00
+649,3.159341E-01,1.259913E-01,0.000000E+00
+650,2.997374E-01,1.193120E-01,0.000000E+00
+651,2.841189E-01,1.128820E-01,0.000000E+00
+652,2.691053E-01,1.067113E-01,0.000000E+00
+653,2.547077E-01,1.008052E-01,0.000000E+00
+654,2.409319E-01,9.516653E-02,0.000000E+00
+655,2.277792E-01,8.979594E-02,0.000000E+00
+656,2.152431E-01,8.469044E-02,0.000000E+00
+657,2.033010E-01,7.984009E-02,0.000000E+00
+658,1.919276E-01,7.523372E-02,0.000000E+00
+659,1.810987E-01,7.086061E-02,0.000000E+00
+660,1.707914E-01,6.671045E-02,0.000000E+00
+661,1.609842E-01,6.277360E-02,0.000000E+00
+662,1.516577E-01,5.904179E-02,0.000000E+00
+663,1.427936E-01,5.550703E-02,0.000000E+00
+664,1.343737E-01,5.216139E-02,0.000000E+00
+665,1.263808E-01,4.899699E-02,0.000000E+00
+666,1.187979E-01,4.600578E-02,0.000000E+00
+667,1.116088E-01,4.317885E-02,0.000000E+00
+668,1.047975E-01,4.050755E-02,0.000000E+00
+669,9.834835E-02,3.798376E-02,0.000000E+00
+670,9.224597E-02,3.559982E-02,0.000000E+00
+671,8.647506E-02,3.334856E-02,0.000000E+00
+672,8.101986E-02,3.122332E-02,0.000000E+00
+673,7.586514E-02,2.921780E-02,0.000000E+00
+674,7.099633E-02,2.732601E-02,0.000000E+00
+675,6.639960E-02,2.554223E-02,0.000000E+00
+676,6.206225E-02,2.386121E-02,0.000000E+00
+677,5.797409E-02,2.227859E-02,0.000000E+00
+678,5.412533E-02,2.079020E-02,0.000000E+00
+679,5.050600E-02,1.939185E-02,0.000000E+00
+680,4.710606E-02,1.807939E-02,0.000000E+00
+681,4.391411E-02,1.684817E-02,0.000000E+00
+682,4.091411E-02,1.569188E-02,0.000000E+00
+683,3.809067E-02,1.460446E-02,0.000000E+00
+684,3.543034E-02,1.358062E-02,0.000000E+00
+685,3.292138E-02,1.261573E-02,0.000000E+00
+686,3.055672E-02,1.170696E-02,0.000000E+00
+687,2.834146E-02,1.085608E-02,0.000000E+00
+688,2.628033E-02,1.006476E-02,0.000000E+00
+689,2.437465E-02,9.333376E-03,0.000000E+00
+690,2.262306E-02,8.661284E-03,0.000000E+00
+691,2.101935E-02,8.046048E-03,0.000000E+00
+692,1.954647E-02,7.481130E-03,0.000000E+00
+693,1.818727E-02,6.959987E-03,0.000000E+00
+694,1.692727E-02,6.477070E-03,0.000000E+00
+695,1.575417E-02,6.027677E-03,0.000000E+00
+696,1.465854E-02,5.608169E-03,0.000000E+00
+697,1.363571E-02,5.216691E-03,0.000000E+00
+698,1.268205E-02,4.851785E-03,0.000000E+00
+699,1.179394E-02,4.512008E-03,0.000000E+00
+700,1.096778E-02,4.195941E-03,0.000000E+00
+701,1.019964E-02,3.902057E-03,0.000000E+00
+702,9.484317E-03,3.628371E-03,0.000000E+00
+703,8.816851E-03,3.373005E-03,0.000000E+00
+704,8.192921E-03,3.134315E-03,0.000000E+00
+705,7.608750E-03,2.910864E-03,0.000000E+00
+706,7.061391E-03,2.701528E-03,0.000000E+00
+707,6.549509E-03,2.505796E-03,0.000000E+00
+708,6.071970E-03,2.323231E-03,0.000000E+00
+709,5.627476E-03,2.153333E-03,0.000000E+00
+710,5.214608E-03,1.995557E-03,0.000000E+00
+711,4.831848E-03,1.849316E-03,0.000000E+00
+712,4.477579E-03,1.713976E-03,0.000000E+00
+713,4.150166E-03,1.588899E-03,0.000000E+00
+714,3.847988E-03,1.473453E-03,0.000000E+00
+715,3.569452E-03,1.367022E-03,0.000000E+00
+716,3.312857E-03,1.268954E-03,0.000000E+00
+717,3.076022E-03,1.178421E-03,0.000000E+00
+718,2.856894E-03,1.094644E-03,0.000000E+00
+719,2.653681E-03,1.016943E-03,0.000000E+00
+720,2.464821E-03,9.447269E-04,0.000000E+00
+721,2.289060E-03,8.775171E-04,0.000000E+00
+722,2.125694E-03,8.150438E-04,0.000000E+00
+723,1.974121E-03,7.570755E-04,0.000000E+00
+724,1.833723E-03,7.033755E-04,0.000000E+00
+725,1.703876E-03,6.537050E-04,0.000000E+00
+726,1.583904E-03,6.078048E-04,0.000000E+00
+727,1.472939E-03,5.653435E-04,0.000000E+00
+728,1.370151E-03,5.260046E-04,0.000000E+00
+729,1.274803E-03,4.895061E-04,0.000000E+00
+730,1.186238E-03,4.555970E-04,0.000000E+00
+731,1.103871E-03,4.240548E-04,0.000000E+00
+732,1.027194E-03,3.946860E-04,0.000000E+00
+733,9.557493E-04,3.673178E-04,0.000000E+00
+734,8.891262E-04,3.417941E-04,0.000000E+00
+735,8.269535E-04,3.179738E-04,0.000000E+00
+736,7.689351E-04,2.957441E-04,0.000000E+00
+737,7.149425E-04,2.750558E-04,0.000000E+00
+738,6.648590E-04,2.558640E-04,0.000000E+00
+739,6.185421E-04,2.381142E-04,0.000000E+00
+740,5.758303E-04,2.217445E-04,0.000000E+00
+741,5.365046E-04,2.066711E-04,0.000000E+00
+742,5.001842E-04,1.927474E-04,0.000000E+00
+743,4.665005E-04,1.798315E-04,0.000000E+00
+744,4.351386E-04,1.678023E-04,0.000000E+00
+745,4.058303E-04,1.565566E-04,0.000000E+00
+746,3.783733E-04,1.460168E-04,0.000000E+00
+747,3.526892E-04,1.361535E-04,0.000000E+00
+748,3.287199E-04,1.269451E-04,0.000000E+00
+749,3.063998E-04,1.183671E-04,0.000000E+00
+750,2.856577E-04,1.103928E-04,0.000000E+00
+751,2.664108E-04,1.029908E-04,0.000000E+00
+752,2.485462E-04,9.611836E-05,0.000000E+00
+753,2.319529E-04,8.973323E-05,0.000000E+00
+754,2.165300E-04,8.379694E-05,0.000000E+00
+755,2.021853E-04,7.827442E-05,0.000000E+00
+756,1.888338E-04,7.313312E-05,0.000000E+00
+757,1.763935E-04,6.834142E-05,0.000000E+00
+758,1.647895E-04,6.387035E-05,0.000000E+00
+759,1.539542E-04,5.969389E-05,0.000000E+00
+760,1.438270E-04,5.578862E-05,0.000000E+00
+761,1.343572E-04,5.213509E-05,0.000000E+00
+762,1.255141E-04,4.872179E-05,0.000000E+00
+763,1.172706E-04,4.553845E-05,0.000000E+00
+764,1.095983E-04,4.257443E-05,0.000000E+00
+765,1.024685E-04,3.981884E-05,0.000000E+00
+766,9.584715E-05,3.725877E-05,0.000000E+00
+767,8.968316E-05,3.487467E-05,0.000000E+00
+768,8.392734E-05,3.264765E-05,0.000000E+00
+769,7.853708E-05,3.056140E-05,0.000000E+00
+770,7.347551E-05,2.860175E-05,0.000000E+00
+771,6.871576E-05,2.675841E-05,0.000000E+00
+772,6.425257E-05,2.502943E-05,0.000000E+00
+773,6.008292E-05,2.341373E-05,0.000000E+00
+774,5.620098E-05,2.190914E-05,0.000000E+00
+775,5.259870E-05,2.051259E-05,0.000000E+00
+776,4.926279E-05,1.921902E-05,0.000000E+00
+777,4.616623E-05,1.801796E-05,0.000000E+00
+778,4.328212E-05,1.689899E-05,0.000000E+00
+779,4.058715E-05,1.585309E-05,0.000000E+00
+780,3.806114E-05,1.487243E-05,0.000000E+00
+781,3.568818E-05,1.395085E-05,0.000000E+00
+782,3.346023E-05,1.308528E-05,0.000000E+00
+783,3.137090E-05,1.227327E-05,0.000000E+00
+784,2.941371E-05,1.151233E-05,0.000000E+00
+785,2.758222E-05,1.080001E-05,0.000000E+00
+786,2.586951E-05,1.013364E-05,0.000000E+00
+787,2.426701E-05,9.509919E-06,0.000000E+00
+788,2.276639E-05,8.925630E-06,0.000000E+00
+789,2.136009E-05,8.377852E-06,0.000000E+00
+790,2.004122E-05,7.863920E-06,0.000000E+00
+791,1.880380E-05,7.381539E-06,0.000000E+00
+792,1.764358E-05,6.929096E-06,0.000000E+00
+793,1.655671E-05,6.505136E-06,0.000000E+00
+794,1.553939E-05,6.108221E-06,0.000000E+00
+795,1.458792E-05,5.736935E-06,0.000000E+00
+796,1.369853E-05,5.389831E-06,0.000000E+00
+797,1.286705E-05,5.065269E-06,0.000000E+00
+798,1.208947E-05,4.761667E-06,0.000000E+00
+799,1.136207E-05,4.477561E-06,0.000000E+00
+800,1.068141E-05,4.211597E-06,0.000000E+00
+801,1.004411E-05,3.962457E-06,0.000000E+00
+802,9.446399E-06,3.728674E-06,0.000000E+00
+803,8.884754E-06,3.508881E-06,0.000000E+00
+804,8.356050E-06,3.301868E-06,0.000000E+00
+805,7.857521E-06,3.106561E-06,0.000000E+00
+806,7.386996E-06,2.922119E-06,0.000000E+00
+807,6.943576E-06,2.748208E-06,0.000000E+00
+808,6.526548E-06,2.584560E-06,0.000000E+00
+809,6.135087E-06,2.430867E-06,0.000000E+00
+810,5.768284E-06,2.286786E-06,0.000000E+00
+811,5.425069E-06,2.151905E-06,0.000000E+00
+812,5.103974E-06,2.025656E-06,0.000000E+00
+813,4.803525E-06,1.907464E-06,0.000000E+00
+814,4.522350E-06,1.796794E-06,0.000000E+00
+815,4.259166E-06,1.693147E-06,0.000000E+00
+816,4.012715E-06,1.596032E-06,0.000000E+00
+817,3.781597E-06,1.504903E-06,0.000000E+00
+818,3.564496E-06,1.419245E-06,0.000000E+00
+819,3.360236E-06,1.338600E-06,0.000000E+00
+820,3.167765E-06,1.262556E-06,0.000000E+00
+821,2.986206E-06,1.190771E-06,0.000000E+00
+822,2.814999E-06,1.123031E-06,0.000000E+00
+823,2.653663E-06,1.059151E-06,0.000000E+00
+824,2.501725E-06,9.989507E-07,0.000000E+00
+825,2.358723E-06,9.422514E-07,0.000000E+00
+826,2.224206E-06,8.888804E-07,0.000000E+00
+827,2.097737E-06,8.386690E-07,0.000000E+00
+828,1.978894E-06,7.914539E-07,0.000000E+00
+829,1.867268E-06,7.470770E-07,0.000000E+00
+830,1.762465E-06,7.053860E-07,0.000000E+00
diff --git a/docs/gifs/doc_cividis.gif b/docs/gifs/doc_cividis.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_cividis.gif differ
diff --git a/docs/gifs/doc_greyscale.gif b/docs/gifs/doc_greyscale.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_greyscale.gif differ
diff --git a/docs/gifs/doc_hsv.gif b/docs/gifs/doc_hsv.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_hsv.gif differ
diff --git a/docs/gifs/doc_hsvMatlab.gif b/docs/gifs/doc_hsvMatlab.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_hsvMatlab.gif differ
diff --git a/docs/gifs/doc_inferno.gif b/docs/gifs/doc_inferno.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_inferno.gif differ
diff --git a/docs/gifs/doc_jet.gif b/docs/gifs/doc_jet.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_jet.gif differ
diff --git a/docs/gifs/doc_latex.gif b/docs/gifs/doc_latex.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_latex.gif differ
diff --git a/docs/gifs/doc_latexAlign.gif b/docs/gifs/doc_latexAlign.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_latexAlign.gif differ
diff --git a/docs/gifs/doc_magma.gif b/docs/gifs/doc_magma.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_magma.gif differ
diff --git a/docs/gifs/doc_parula.gif b/docs/gifs/doc_parula.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_parula.gif differ
diff --git a/docs/gifs/doc_pause.gif b/docs/gifs/doc_pause.gif
Binary files a/docs/gifs/doc_pause.gif and b/docs/gifs/doc_pause.gif differ
diff --git a/docs/gifs/doc_plasma.gif b/docs/gifs/doc_plasma.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_plasma.gif differ
diff --git a/docs/gifs/doc_sinebow.gif b/docs/gifs/doc_sinebow.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_sinebow.gif differ
diff --git a/docs/gifs/doc_turbo.gif b/docs/gifs/doc_turbo.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_turbo.gif differ
diff --git a/docs/gifs/doc_viridis.gif b/docs/gifs/doc_viridis.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_viridis.gif differ
diff --git a/docs/gifs/doc_xelatex.gif b/docs/gifs/doc_xelatex.gif
new file mode 100644
Binary files /dev/null and b/docs/gifs/doc_xelatex.gif differ
diff --git a/reanimate.cabal b/reanimate.cabal
--- a/reanimate.cabal
+++ b/reanimate.cabal
@@ -1,8 +1,9 @@
+cabal-version:       1.18
 -- Initial reani.cabal generated by cabal init.  For further documentation,
 --  see http://haskell.org/cabal/users-guide/
 
 name:                reanimate
-version:             0.1.7.0
+version:             0.1.8.0
 -- synopsis:
 -- description:
 license:             PublicDomain
@@ -14,8 +15,8 @@
 build-type:          Simple
 extra-source-files:  ChangeLog.md
 extra-doc-files:     docs/gifs/*.gif
-cabal-version:       >=1.18
 
+
 description:
   Animation library based on SVGs. Can import (and manipulate) SVGs from
   LaTeX and diagrams. Exports gifs, mp4s, and more. Ships with a webbased
@@ -29,6 +30,7 @@
                       viewer/build/static/js/runtime~main.9eb600ee.js
                       viewer/build/static/css/main.f7ad3e9b.chunk.css
                       data/CIExyz.csv
+                      data/CIE_XYZ.csv
                       data/cone_sensitivity_lms.csv
 
 Source-Repository head
@@ -63,6 +65,7 @@
                       Reanimate.Scene
                       Reanimate.Povray
                       Reanimate.Effect
+                      Reanimate.Builtin.TernaryPlot
                       Reanimate.Constants
                       Reanimate.Chiphunk
                       Reanimate.PolyShape
@@ -75,13 +78,13 @@
                       Paths_reanimate
   build-depends:       base >=4.10 && <4.13,
                        time, text, filepath, process, directory,
-                       containers, reanimate-svg >= 0.9.2.1, xml, bytestring, lens, linear, mtl, matrix,
+                       containers, reanimate-svg >= 0.9.3.0, xml, bytestring, lens, linear, mtl, matrix,
                        JuicyPixels, attoparsec, parallel, diagrams, diagrams-svg,
                        diagrams-core, diagrams-lib, diagrams-contrib,
                        svg-builder, cubicbezier, palette, websockets,
                        hashable, fsnotify, open-browser, random-shuffle, base64-bytestring,
                        vector, colour, cassava, ansi-wl-pprint, here, temporary,
-                       optparse-applicative, chiphunk
+                       optparse-applicative, chiphunk >= 0.1.2.0
   ghc-options: -Wall
 
 test-suite spec
diff --git a/src/Reanimate.hs b/src/Reanimate.hs
--- a/src/Reanimate.hs
+++ b/src/Reanimate.hs
@@ -57,6 +57,21 @@
     bellS,
     oscillateS,
     fromListS,
+    -- ** Scenes
+    (#),
+    ZIndex,
+    Scene,
+    sceneAnimation,
+    fork,
+    play,
+    playZ,
+    queryNow,
+    waitAll,
+    waitUntil,
+    wait,
+    adjustZ ,
+    withSceneDuration,
+
     -- * SVG
     module Reanimate.Svg.Constructors,
     module Reanimate.Svg.LineCommand,
@@ -104,3 +119,4 @@
 import           Reanimate.Svg.BoundingBox
 import           Reanimate.Svg.Constructors
 import           Reanimate.Svg.LineCommand
+import           Reanimate.Scene
diff --git a/src/Reanimate/Animation.hs b/src/Reanimate/Animation.hs
--- a/src/Reanimate/Animation.hs
+++ b/src/Reanimate/Animation.hs
@@ -3,12 +3,14 @@
 import           Control.Arrow              ()
 import           Data.Fixed                 (mod')
 import qualified Data.Map                   as M
-import           Graphics.SvgTree           (Document (..), Number (..),
+import           Graphics.SvgTree           (Alignment (..), Document (..),
+                                             Number (..),
+                                             PreserveAspectRatio (..),
                                              Tree (..), xmlOfTree)
 import           Graphics.SvgTree.Printer
 import           Reanimate.Constants
-import           Reanimate.Svg.Constructors
 import           Reanimate.Signal
+import           Reanimate.Svg.Constructors
 import           Text.XML.Light.Output
 
 -- | Duration of an animation or effect. Usually measured in seconds.
@@ -110,7 +112,7 @@
 --
 --   Example:
 --
---   > pause 1 `seqA` drawCircle
+--   > pause 1 `seqA` drawProgress
 --
 --   <<docs/gifs/doc_pause.gif>>
 pause :: Duration -> Animation
@@ -149,6 +151,7 @@
       , _definitions = M.empty
       , _description = ""
       , _documentLocation = ""
+      , _documentAspectRatio = PreserveAspectRatio False AlignNone Nothing
       }
 
 -- | Map over the SVG produced by an animation at every frame.
@@ -192,6 +195,11 @@
 --   <<docs/gifs/doc_pauseAround.gif>>
 pauseAround :: Duration -> Duration -> Animation -> Animation
 pauseAround start end = pauseAtEnd end . pauseAtBeginning start
+
+-- XXX: Rename to 'setDurationFreeze'. Add 'setDurationDrop' and
+--      'setDurationLoop'.
+pauseUntil :: Duration -> Animation -> Animation
+pauseUntil d a = pauseAtEnd (d-duration a) a
 
 -- Freeze frame at time @t@.
 freezeFrame :: Double -> Animation -> (Time -> SVG)
diff --git a/src/Reanimate/Builtin/Documentation.hs b/src/Reanimate/Builtin/Documentation.hs
--- a/src/Reanimate/Builtin/Documentation.hs
+++ b/src/Reanimate/Builtin/Documentation.hs
@@ -9,7 +9,7 @@
   [ mkBackground "white"
   , withFillOpacity 0 $
     withStrokeWidth 0.1 $
-    withStrokeColor "black" svg ]
+    withStrokeColor "black" (mkGroup [svg]) ]
 
 -- | <<docs/gifs/doc_drawBox.gif>>
 drawBox :: Animation
@@ -21,6 +21,11 @@
 drawCircle :: Animation
 drawCircle = mkAnimation 2 $ \t ->
   partialSvg t $ pathify $
+  mkCircle (screenHeight/3)
+
+drawBall :: Animation
+drawBall = mkAnimation 2 $ \t ->
+  scale t $ withFillOpacity 1 $ withFillColor "red" $
   mkCircle (screenHeight/3)
 
 -- | <<docs/gifs/doc_drawProgress.gif>>
diff --git a/src/Reanimate/Builtin/TernaryPlot.hs b/src/Reanimate/Builtin/TernaryPlot.hs
new file mode 100644
--- /dev/null
+++ b/src/Reanimate/Builtin/TernaryPlot.hs
@@ -0,0 +1,65 @@
+module Reanimate.Builtin.TernaryPlot where
+
+import           Codec.Picture
+import           Graphics.SvgTree (Tree)
+import           Reanimate.Raster
+import           Reanimate.Svg
+
+-- (top, left, right)
+-- a+b+c=1
+type ACoord = Double
+type BCoord = Double
+type CCoord = Double
+
+-- Creates a centered ternary plot with a width of 5.
+raster :: Int -> (ACoord -> BCoord -> CCoord -> PixelRGBA8) -> Tree
+raster density fn =
+    scaleToWidth stdWidth $
+    translate (-cX) (-cY) $
+    scaleToWidth 1 $
+    flipYAxis $
+    translate (fromIntegral density/2) (-fromIntegral height/2) $
+    embedImage $ generateImage gen density height
+  where
+    stdWidth = 5
+    (cX, cY) = toCartesianCoords (1/3) (1/3)
+    height = round (fromIntegral density * (sqrt 3 / 2) :: Double)
+    gen x y =
+      let
+          x' = (fromIntegral x / fromIntegral density)
+          y' = (fromIntegral y / fromIntegral density)
+          aCoord = (x'*2-bCoord)/2
+          bCoord = y' / (sqrt 3 / 2)
+          cCoord = 1 - aCoord - bCoord
+      in if aCoord + bCoord > 1 || aCoord < 0 || bCoord < 0 || cCoord < 0
+        then PixelRGBA8 0 0 0 0
+        else fn aCoord bCoord cCoord
+
+atCenter :: Double -> Tree -> Tree
+atCenter stdWidth = translate (-cX*stdWidth) (-cY*stdWidth)
+  where
+    (cX, cY) = toCartesianCoords (1/3) (1/3)
+
+radius :: Double
+radius = sqrt (cX*cX + cY*cY)
+  where
+    (cX, cY) = toCartesianCoords (1/3) (1/3)
+
+toCartesianCoords :: ACoord -> BCoord -> (Double, Double)
+toCartesianCoords a b = (x, y)
+  where
+    x = (a+2*b)/2
+    y = (sqrt 3 / 2) * a
+
+toOffsetCartesianCoords :: ACoord -> BCoord -> (Double, Double)
+toOffsetCartesianCoords a b =
+    (tx-zx, ty-zy)
+  where
+    (zx,zy) = toCartesianCoords (1/3) (1/3)
+    (tx,ty) = toCartesianCoords a b
+
+fromCartesianCoords :: Double -> Double -> (ACoord, BCoord, CCoord)
+fromCartesianCoords x y = (a,b,1-a-b)
+  where
+    a = (x*2-b)/2
+    b = y / (sqrt 3 / 2)
diff --git a/src/Reanimate/ColorMap.hs b/src/Reanimate/ColorMap.hs
--- a/src/Reanimate/ColorMap.hs
+++ b/src/Reanimate/ColorMap.hs
@@ -26,6 +26,8 @@
 
 -- | Given a number t in the range [0,1], returns the corresponding color from
 --   the “turbo” color scheme by Anton Mikhailov.
+--
+--   <<docs/gifs/doc_turbo.gif>>
 turbo :: Double -> PixelRGB8
 turbo t = PixelRGB8 red green blue
   where
@@ -38,6 +40,8 @@
 -- | Given a number t in the range [0,1], returns the corresponding color from
 --   the “viridis” perceptually-uniform color scheme designed by van der Walt,
 --   Smith and Firing for matplotlib, represented as an RGB string.
+--
+--   <<docs/gifs/doc_viridis.gif>>
 viridis :: Double -> PixelRGB8
 viridis = ramp (colors
   "44015444025645045745055946075a46085c460a5d460b5e470d60470e614710634711644713\
@@ -65,6 +69,8 @@
 -- | Given a number t in the range [0,1], returns the corresponding color from
 --   the “magma” perceptually-uniform color scheme designed by van der Walt and
 --   Smith for matplotlib, represented as an RGB string.
+--
+--   <<docs/gifs/doc_magma.gif>>
 magma :: Double -> PixelRGB8
 magma = ramp (colors
   "00000401000501010601010802010902020b02020d03030f0303120404140504160605180605\
@@ -92,6 +98,8 @@
 -- | Given a number t in the range [0,1], returns the corresponding color from
 --   the “inferno” perceptually-uniform color scheme designed by van der Walt
 --   and Smith for matplotlib, represented as an RGB string.
+--
+--   <<docs/gifs/doc_inferno.gif>>
 inferno :: Double -> PixelRGB8
 inferno = ramp (colors
   "00000401000501010601010802010a02020c02020e0302100403120403140504170604190705\
@@ -119,6 +127,8 @@
 -- | Given a number t in the range [0,1], returns the corresponding color from
 --   the “plasma” perceptually-uniform color scheme designed by van der Walt and
 --   Smith for matplotlib, represented as an RGB string.
+--
+--   <<docs/gifs/doc_plasma.gif>>
 plasma :: Double -> PixelRGB8
 plasma = ramp (colors
   "0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05\
@@ -145,6 +155,8 @@
 
 -- | Given a number t in the range [0,1], returns the corresponding color from
 --   the “sinebow” color scheme by Jim Bumgardner and Charlie Loyd.
+--
+--   <<docs/gifs/doc_sinebow.gif>>
 sinebow :: Double -> PixelRGB8
 sinebow t = PixelRGB8 r g b
   where
@@ -158,6 +170,8 @@
 -- | Given a number t in the range [0,1], returns the corresponding color from
 --   the “cividis” color vision deficiency-optimized color scheme designed by
 --   Nuñez, Anderton, and Renslow, represented as an RGB string.
+--
+--   <<docs/gifs/doc_cividis.gif>>
 cividis :: Double -> PixelRGB8
 cividis t = PixelRGB8 red green blue
   where
@@ -168,6 +182,8 @@
     trunc = fromIntegral . min 255 . max 0
 
 -- | Jet colormap. Used to be the default in matlab. Obsolete.
+--
+--   <<docs/gifs/doc_jet.gif>>
 jet :: Double -> PixelRGB8
 jet t = PixelRGB8 red green blue
   where
@@ -178,22 +194,32 @@
     trunc = round . min 255 . max 0 . (*) 255
 
 -- | hsv colormap. Goes from 0 degrees to 360 degrees.
+--
+--   <<docs/gifs/doc_hsv.gif>>
 hsv :: Double -> PixelRGB8
 hsv t = PixelRGB8 (round $ r*255) (round $ g*255) (round $ b*255)
   where
     RGB r g b = HSV.hsv (t * 360) 1 1
 
 -- | Matlab hsv colormap. Goes from 0 degrees to 330 degrees.
+--
+--   <<docs/gifs/doc_hsvMatlab.gif>>
 hsvMatlab :: Double -> PixelRGB8
 hsvMatlab t = PixelRGB8 (round $ r*255) (round $ g*255) (round $ b*255)
   where
     RGB r g b = HSV.hsv (t * 330) 1 1
 
+-- | Greyscale colormap.
+--
+--   <<docs/gifs/doc_greyscale.gif>>
 greyscale :: Double -> PixelRGB8
 greyscale t = PixelRGB8 v v v
   where
     v = round $ t * 255
 
+-- | Parula is the default colormap for matlab.
+--
+--   <<docs/gifs/doc_parula.gif>>
 parula :: Double -> PixelRGB8
 parula = ramp vec
   where
diff --git a/src/Reanimate/ColorSpace.hs b/src/Reanimate/ColorSpace.hs
--- a/src/Reanimate/ColorSpace.hs
+++ b/src/Reanimate/ColorSpace.hs
@@ -25,6 +25,14 @@
     Right vec -> return $ Map.fromList
       [ (nm, (x,y,z)) | (nm,x,y,z) <- V.toList vec, nm <= 700 ]
 
+bigXYZCoordinates :: Map Nanometer (Double, Double, Double)
+bigXYZCoordinates = unsafePerformIO $ do
+  dat <- BS.readFile =<< getDataFileName "data/CIE_XYZ.csv"
+  case decode NoHeader dat of
+    Left err -> error err
+    Right vec -> return $ Map.fromList
+      [ (nm, (x,y,z)) | (nm,x,y,z) <- V.toList vec, nm <= 700 ]
+
 renderXYZCoordinates :: Tree
 renderXYZCoordinates =
   withFillOpacity 0 $
@@ -53,7 +61,7 @@
   case decode NoHeader dat of
     Left err -> error err
     Right vec -> return $ Map.fromList
-      [ (nm, (l,m,fromMaybe 0 s)) | (nm,l,m,s) <- V.toList vec ]
+      [ (nm, (l,m,fromMaybe 0 s)) | (nm,l,m,s) <- V.toList vec, nm <= 700 ]
 
 renderSensitivity :: Tree
 renderSensitivity = mkGroup
diff --git a/src/Reanimate/Driver.hs b/src/Reanimate/Driver.hs
--- a/src/Reanimate/Driver.hs
+++ b/src/Reanimate/Driver.hs
@@ -18,21 +18,25 @@
 presetFormat Youtube    = RenderMp4
 presetFormat ExampleGif = RenderGif
 presetFormat Quick      = RenderMp4
+presetFormat MediumQ    = RenderMp4
+presetFormat HighQ      = RenderMp4
 
 presetFPS :: Preset -> FPS
 presetFPS Youtube    = 60
 presetFPS ExampleGif = 24
 presetFPS Quick      = 15
+presetFPS MediumQ    = 30
+presetFPS HighQ      = 30
 
 presetWidth :: Preset -> Width
 presetWidth Youtube    = 2560
 presetWidth ExampleGif = 320
 presetWidth Quick      = 320
+presetWidth MediumQ    = 800
+presetWidth HighQ      = 1920
 
 presetHeight :: Preset -> Height
-presetHeight Youtube    = 1440
-presetHeight ExampleGif = 180
-presetHeight Quick      = 180
+presetHeight preset = presetWidth preset * 9 `div` 16
 
 formatFPS :: Format -> FPS
 formatFPS RenderMp4  = 60
diff --git a/src/Reanimate/Driver/CLI.hs b/src/Reanimate/Driver/CLI.hs
--- a/src/Reanimate/Driver/CLI.hs
+++ b/src/Reanimate/Driver/CLI.hs
@@ -33,7 +33,7 @@
     }
    deriving (Show)
 
-data Preset = Youtube | ExampleGif | Quick
+data Preset = Youtube | ExampleGif | Quick | MediumQ | HighQ
   deriving (Show)
 
 readFormat :: String -> Maybe Format
@@ -55,12 +55,16 @@
     "youtube" -> Just Youtube
     "gif"     -> Just ExampleGif
     "quick"   -> Just Quick
+    "medium"  -> Just MediumQ
+    "high"    -> Just HighQ
     _         -> Nothing
 
 showPreset :: Preset -> String
 showPreset Youtube    = "youtube"
 showPreset ExampleGif = "gif"
 showPreset Quick      = "quick"
+showPreset MediumQ    = "medium"
+showPreset HighQ      = "high"
 
 options :: Parser Options
 options = Options <$> commandP
@@ -91,19 +95,19 @@
     parse = pure Test
 
 checkCommand :: ParserInfo Command
-checkCommand = info (parse)
+checkCommand = info parse
     (progDesc "Run a system's diagnostic and report any missing external dependencies.")
   where
     parse = pure Check
 
 viewCommand :: ParserInfo Command
-viewCommand = info (parse)
+viewCommand = info parse
     (progDesc "Play animation in browser window.")
   where
     parse = pure View
 
 renderCommand :: ParserInfo Command
-renderCommand = info (parse)
+renderCommand = info parse
     (progDesc "Render animation to file.")
   where
     -- fromPreset :: (Maybe Preset -> (Command -> Command))
@@ -137,7 +141,7 @@
       <*> optional (option (maybeReader readPreset)
           (long "preset" <> showDefaultWith showPreset
           <> metavar "TYPE"
-          <> help "Parameter presets: youtube, gif, quick"))
+          <> help "Parameter presets: youtube, gif, quick, medium, high"))
 
 opts :: ParserInfo Options
 opts = info (options <**> helper )
diff --git a/src/Reanimate/Driver/Check.hs b/src/Reanimate/Driver/Check.hs
--- a/src/Reanimate/Driver/Check.hs
+++ b/src/Reanimate/Driver/Check.hs
@@ -26,7 +26,7 @@
     runCheck "Has XeLaTeX" hasXeLaTeX
     runCheck "Has dvisvgm" hasDvisvgm
     runCheck "Has povray" hasPovray
-    runCheck ("Has LaTeX package '"++ "babel" ++ "'") $ hasTeXPackage "latex" $
+    runCheck ("Has LaTeX package '"++ "babel" ++ "'") $ hasTeXPackage "latex"
       "[english]{babel}"
     forM_ latexPackages $ \pkg ->
       runCheck ("Has LaTeX package '"++ pkg ++ "'") $ hasTeXPackage "latex" $
diff --git a/src/Reanimate/Driver/Server.hs b/src/Reanimate/Driver/Server.hs
--- a/src/Reanimate/Driver/Server.hs
+++ b/src/Reanimate/Driver/Server.hs
@@ -109,7 +109,7 @@
     expectFrame :: Either String Text -> IO (Integer, Text)
     expectFrame (Left "") = do
       sendTextData conn (T.pack "Done")
-      exitWith ExitSuccess
+      exitSuccess
     expectFrame (Left err) = do
       sendTextData conn $ T.pack $ "Error" ++ err
       exitWith (ExitFailure 1)
diff --git a/src/Reanimate/LaTeX.hs b/src/Reanimate/LaTeX.hs
--- a/src/Reanimate/LaTeX.hs
+++ b/src/Reanimate/LaTeX.hs
@@ -15,14 +15,39 @@
 import           System.FilePath   (replaceExtension, takeFileName, (</>))
 import           System.IO.Unsafe  (unsafePerformIO)
 
+-- | Invoke latex and import the result as an SVG object. SVG objects are
+--   cached to improve performance.
+--
+--   Example:
+--
+--   > latex "$e^{i\\pi}+1=0$"
+--
+--   <<docs/gifs/doc_latex.gif>>
 latex :: T.Text -> Tree
 latex tex = (unsafePerformIO . (cacheMem . cacheDiskSvg) latexToSVG)
   ("% plain latex\n" <> tex)
 
+-- | Invoke xelatex and import the result as an SVG object. SVG objects are
+--   cached to improve performance. Xelatex has support for non-western scripts.
+--
+--   Example:
+--
+--   > xelatex "中文"
+--
+--   <<docs/gifs/doc_xelatex.gif>>
 xelatex :: Text -> Tree
 xelatex tex = (unsafePerformIO . (cacheMem . cacheDiskSvg) xelatexToSVG)
   ("% xelatex\n" <> tex)
 
+-- | Invoke latex and import the result as an SVG object. SVG objects are
+--   cached to improve performance. This wraps the TeX code in an 'align*'
+--   context.
+--
+--   Example:
+--
+--   > latexAlign "R = \\frac{{\\Delta x}}{{kA}}"
+--
+--   <<docs/gifs/doc_latexAlign.gif>>
 latexAlign :: Text -> Tree
 latexAlign tex = latex $ T.unlines ["\\begin{align*}", tex, "\\end{align*}"]
 
diff --git a/src/Reanimate/Raster.hs b/src/Reanimate/Raster.hs
--- a/src/Reanimate/Raster.hs
+++ b/src/Reanimate/Raster.hs
@@ -16,13 +16,13 @@
 import qualified Graphics.SvgTree            as Svg
 import           Reanimate.Misc
 import           Reanimate.Animation
+import           Reanimate.Svg.Constructors
 import           System.FilePath
 import           System.IO
 import           System.IO.Temp
 import           System.IO.Unsafe
 
 
--- XXX: Use Px instead of Num for width and height?
 {-# INLINE embedImage #-}
 embedImage :: PngSavable a => Image a -> Tree
 embedImage img = embedPng width height (encodePng img)
@@ -31,8 +31,9 @@
     height = fromIntegral $ imageHeight img
 
 embedPng :: Double -> Double -> LBS.ByteString -> Tree
-embedPng w h png =
+embedPng w h png = flipYAxis $
   ImageTree $ defaultSvg
+    & Svg.imageCornerUpperLeft .~ (Svg.Num (-w/2), Svg.Num (-h/2))
     & Svg.imageWidth .~ Svg.Num w
     & Svg.imageHeight .~ Svg.Num h
     & Svg.imageHref .~ ("data:image/png;base64," ++ imgData)
diff --git a/src/Reanimate/Scene.hs b/src/Reanimate/Scene.hs
--- a/src/Reanimate/Scene.hs
+++ b/src/Reanimate/Scene.hs
@@ -5,11 +5,9 @@
 import           Control.Monad.ST
 import           Data.List
 import           Data.Ord
-import           Debug.Trace
 import           Reanimate.Animation
 import           Reanimate.Svg.Constructors
 
-data World
 type ZIndex = Int
 
 (#) :: a -> (a -> b) -> b
@@ -61,12 +59,6 @@
   where
     (_, s, p, tl) = runST (unM action 0)
 
-debug :: String -> Scene s ()
-debug msg = M $ \_ -> trace msg (return ((), 0, 0, emptyTimeline))
-
-someaction :: Scene s ()
-someaction = debug "someaction"
-
 fork :: Scene s a -> Scene s a
 fork (M action) = M $ \t -> do
   (a, s, p, tl) <- action t
@@ -109,26 +101,3 @@
   s
   t2 <- queryNow
   return (t2-t1)
-
-{-
-blackness
-show numbers
-fade in colormap
-slide to middle
-
-do fork $ play $ setDuration 5 showNumbers
-      # fadeOut 1 # fadeIn 1
-   wait 3
-
-   fork $ do
-    playZ (-1) $ setDuration 5 $ revealImage 0 0.5
-      # pauseAround 1 1
-    playZ (-1) $ setDuration 5 $ revealImage 0.5 1
-      # pauseAtEnd 1
-
-   play $ setDuration 5 $ colormap 0 0.5
-     # fadeIn 1 # pauseAround 1 1
-   play $ setDuration 5 $ colormap 0.5 1
-     # fadeOut 1 # pauseAtEnd 1
-
--}
diff --git a/src/Reanimate/Svg.hs b/src/Reanimate/Svg.hs
--- a/src/Reanimate/Svg.hs
+++ b/src/Reanimate/Svg.hs
@@ -132,7 +132,29 @@
         DefinitionTree{} -> return ()
         _ ->
           modify $ \(n, l, r) -> (n, acc t:l, r)
+{-
+<g transform="translate(10,10)">
+  <g transform="scale(2)">
+    <circle/>
+  </g>
+  <g transform="scale(0.5)">
+    <rect/>
+  </g>
+</g>
 
+[ (\svg -> <g transform="translate(10,10)"><g transform="scale(2)">svg</g></g>, <circle/>)
+, (\svg -> <g transform="translate(10,10)"><g transform="scale(0.5)">svg</g></g>, <rect/>)]
+-}
+svgGlyphs :: Tree -> [(Tree -> Tree, DrawAttributes, Tree)]
+svgGlyphs = worker id defaultSvg
+  where
+    worker acc attr =
+      \case
+        GroupTree g ->
+          let acc' sub = acc (GroupTree $ g & groupChildren .~ [sub])
+              attr' = (g^.drawAttributes) `mappend` attr
+          in concatMap (worker acc' attr') (g ^. groupChildren)
+        t -> [(acc, attr, t)]
 
 pathify :: Tree -> Tree
 pathify = mapTree worker
diff --git a/src/Reanimate/Svg/BoundingBox.hs b/src/Reanimate/Svg/BoundingBox.hs
--- a/src/Reanimate/Svg/BoundingBox.hs
+++ b/src/Reanimate/Svg/BoundingBox.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE LambdaCase #-}
 module Reanimate.Svg.BoundingBox where
 
 import           Control.Arrow
@@ -25,6 +24,16 @@
     worker (minx, miny, maxx, maxy) (V2 x y) =
       (min minx x, min miny y, max maxx x, max maxy y)
 
+svgHeight :: Tree -> Double
+svgHeight t = h
+  where
+    (_w, h, _x, _y) = boundingBox t
+
+svgWidth :: Tree -> Double
+svgWidth t = w
+  where
+    (w, _h, _x, _y) = boundingBox t
+
 linePoints :: [LineCommand] -> [RPoint]
 linePoints = worker zero
   where
@@ -56,7 +65,7 @@
       LineTree line   -> map pointToRPoint [line^.linePoint1, line^.linePoint2]
       RectangleTree rect ->
         case pointToRPoint (rect^.rectUpperLeftCorner) of
-          V2 x y -> [V2 x y] ++
+          V2 x y -> V2 x y :
             case mapTuple (fmap $ toUserUnit defaultDPI) (rect^.rectWidth, rect^.rectHeight) of
               (Just (Num w), Just (Num h)) -> [V2 (x+w) (y+h)]
               _                            -> []
@@ -73,5 +82,5 @@
     mapTuple f = f *** f
     pointToRPoint p =
       case mapTuple (toUserUnit defaultDPI) p of
-        (Num x, Num y) -> (V2 x y)
+        (Num x, Num y) -> V2 x y
         _ -> error "Reanimate.Svg.svgBoundingPoints: Unrecognized number format."
diff --git a/src/Reanimate/Svg/Constructors.hs b/src/Reanimate/Svg/Constructors.hs
--- a/src/Reanimate/Svg/Constructors.hs
+++ b/src/Reanimate/Svg/Constructors.hs
@@ -1,7 +1,7 @@
 module Reanimate.Svg.Constructors where
 
 import           Codec.Picture                (PixelRGBA8 (..))
-import           Control.Lens                 ((&), (.~))
+import           Control.Lens                 ((&), (.~), (?~))
 import           Data.Attoparsec.Text         (parseOnly)
 import qualified Data.Map                     as Map
 import qualified Data.Text                    as T
@@ -15,7 +15,7 @@
 
 withTransformations :: [Transformation] -> Tree -> Tree
 withTransformations transformations t =
-  mkGroup [t] & transform .~ Just transformations
+  mkGroup [t] & transform ?~ transformations
 
 translate :: Double -> Double -> Tree -> Tree
 translate x y = withTransformations [Translate x y]
@@ -56,6 +56,12 @@
 scaleXY :: Double -> Double -> Tree -> Tree
 scaleXY x y = withTransformations [Scale x (Just y)]
 
+flipXAxis :: Tree -> Tree
+flipXAxis = scaleXY (-1) 1
+
+flipYAxis :: Tree -> Tree
+flipYAxis = scaleXY 1 (-1)
+
 center :: Tree -> Tree
 center t = translate (-x-w/2) (-y-h/2) t
   where
@@ -90,10 +96,10 @@
 withFillColorPixel color = fillColor .~ pure (ColorRef color)
 
 withFillOpacity :: Double -> Tree -> Tree
-withFillOpacity opacity = fillOpacity .~ Just (realToFrac opacity)
+withFillOpacity opacity = fillOpacity ?~ realToFrac opacity
 
 withGroupOpacity :: Double -> Tree -> Tree
-withGroupOpacity opacity = groupOpacity .~ Just (realToFrac opacity)
+withGroupOpacity opacity = groupOpacity ?~ realToFrac opacity
 
 withStrokeWidth :: Double -> Tree -> Tree
 withStrokeWidth width = strokeWidth .~ pure (Num width)
@@ -102,13 +108,13 @@
 withClipPathRef ref = clipPathRef .~ pure ref
 
 withId :: String -> Tree -> Tree
-withId idTag = attrId .~ Just idTag
+withId idTag = attrId ?~ idTag
 
 mkRect :: Double -> Double -> Tree
 mkRect width height = translate (-width/2) (-height/2) $ RectangleTree $ defaultSvg
   & rectUpperLeftCorner .~ (Num 0, Num 0)
-  & rectWidth .~ Just (Num width)
-  & rectHeight .~ Just (Num height)
+  & rectWidth ?~ Num width
+  & rectHeight ?~ Num height
 
 mkCircle :: Double -> Tree
 mkCircle radius = CircleTree $ defaultSvg
@@ -132,9 +138,12 @@
 mkUse name = UseTree (defaultSvg & useName .~ name) Nothing
 
 mkClipPath :: String -> [Tree] -> Tree
-mkClipPath idTag forest = withId idTag $ ClipPathTree $ (defaultSvg
+mkClipPath idTag forest = withId idTag $ ClipPathTree (defaultSvg
   & clipPathContent .~ forest)
 
+mkPath :: [PathCommand] -> Tree
+mkPath cmds = PathTree $ defaultSvg & pathDefinition .~ cmds
+
 mkPathString :: String -> Tree
 mkPathString = mkPathText . T.pack
 
@@ -142,7 +151,7 @@
 mkPathText str =
   case parseOnly pathParser str of
     Left err   -> error err
-    Right cmds -> PathTree $ defaultSvg & pathDefinition .~ cmds
+    Right cmds -> mkPath cmds
 
 mkLinePath :: [(Double, Double)] -> Tree
 mkLinePath [] = mkGroup []
@@ -161,8 +170,8 @@
 
 gridLayout :: [[Tree]] -> Tree
 gridLayout rows = mkGroup
-    [ translate (-screenWidth/2+colSep*(nCol))
-                (screenHeight/2-rowSep*(nRow))
+    [ translate (-screenWidth/2+colSep*nCol)
+                (screenHeight/2-rowSep*nRow)
       elt
     | (nRow, col) <- zip [1..] rows
     , let nCols = length col
diff --git a/src/Reanimate/Svg/Unuse.hs b/src/Reanimate/Svg/Unuse.hs
--- a/src/Reanimate/Svg/Unuse.hs
+++ b/src/Reanimate/Svg/Unuse.hs
@@ -3,7 +3,7 @@
   , unbox
   ) where
 
-import           Control.Lens                 ((%~), (&), (.~), (^.))
+import           Control.Lens                 ((%~), (&), (.~), (^.),(?~))
 import qualified Data.Map                     as Map
 import           Graphics.SvgTree             hiding (height, line, path, use,
                                                width)
@@ -25,7 +25,7 @@
         Just tree ->
           GroupTree $
           defaultSvg & groupChildren .~ [tree]
-                     & transform .~ Just [baseToTransformation (use^.useBase)]
+                     & transform ?~ [baseToTransformation (use^.useBase)]
     replace x = x
     baseToTransformation (x,y) =
       case (toUserUnit defaultDPI x, toUserUnit defaultDPI y) of
@@ -44,7 +44,7 @@
 unbox doc@Document{_viewBox = Just (minx, minw, _width, _height)} =
   GroupTree $ defaultSvg
           & groupChildren .~ doc^.elements
-          & transform .~ Just [Translate (-minx) (-minw)]
+          & transform ?~ [Translate (-minx) (-minw)]
 unbox doc =
   GroupTree $ defaultSvg
     & groupChildren .~ doc^.elements
diff --git a/test/UnitTests.hs b/test/UnitTests.hs
--- a/test/UnitTests.hs
+++ b/test/UnitTests.hs
@@ -67,9 +67,10 @@
   files <- getDirectoryContents path
   return $ testGroup "videos"
     [ testCase dir $ do
-        (ret, _stdout, _stderr) <- readProcessWithExitCode "stack" (["ghc","--", fullPath] ++ ghcOpts) ""
+        (ret, _stdout, err) <- readProcessWithExitCode "stack" (["ghc","--", "-i"++path</>dir, fullPath] ++ ghcOpts) ""
+        _ <- evaluate (length err)
         case ret of
-          ExitFailure{} -> assertFailure "Failed to compile"
+          ExitFailure{} -> assertFailure $ "Failed to compile:\n" ++ err
           ExitSuccess   -> return ()
     | dir <- files
     , let fullPath = path </> dir </> dir <.> "hs"
