A Prettier Cart

October 17, 2011

We’re making some changes to Marblespark and decided that it was time to clean up an eye sore; our shopping cart.  To be honest, most of the current cart was a quick and dirty implementation that was done late at night.  As you can see, it is ugly, but functional.  Gee, can you tell there’s an HTML table underneath it.

cart_current

 

Phil’s got a great eye at making things look good.  Not me.  I’m a table with boxes and lines, no round corners or any of that fancy schmancy stuff.  Take a look; I think the differences are striking and I was so blown away by the differences that I decided to write this.  Nice clean look; no harsh lines; less text and a nice pretty book cover image.

cart_new

In a week or so, I’ll have the site updated with the new cart.  We’re heading into our Christmas rush, so we’ll see if we get more compliments or complaints.  My bet is on compliments. 

What do you think; like the new cart look?


Website Log Tail

December 19, 2010

One of the downsides of our shared hosting plan is that we don’t have any access to the box, except for FTP. Marblespark.com uses log4net extensively and the logs are used to track all sorts of things. Since I don’t have any type of RDP or other terminal access to the server, I needed something to view the logs.

First Attempt

The first attempt was a very, very basic asp.net page with a drop down for the log files, a text box for number of lines to read, a text area to display the lines and one button “View Log”. Seemed very simple, until I needed to read a log file that was already opened for writing.

fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

Next, I needed a way to read a file backward. Gee, how hard can that be… Luckily, I was able to find the code (forgot where) and here’s the guts of it. Search on c# backward reader and you’ll find it.

   1: public string Readline()

   2: {

   3:     byte[] line;

   4:     byte[] text = new byte[1];

   5:     long position = 0;

   6:     int count;

   7:     fs.Seek(0, SeekOrigin.Current);

   8:     position = fs.Position;

   9:

  10:     //do we have trailing \r\n?

  11:     if (fs.Length > 1)

  12:     {

  13:         byte[] vagnretur = new byte[2];

  14:         fs.Seek(-2, SeekOrigin.Current);

  15:         fs.Read(vagnretur, 0, 2);

  16:         if (ASCIIEncoding.ASCII.GetString(vagnretur).Equals("\r\n"))

  17:         {

  18:             //move it back

  19:             fs.Seek(-2, SeekOrigin.Current);

  20:             position = fs.Position;

  21:         }

  22:     }

  23:

  24:     while (fs.Position > 0)

  25:     {

  26:         text.Initialize();

  27:         //read one char

  28:         fs.Read(text, 0, 1);

  29:         string asciiText = ASCIIEncoding.ASCII.GetString(text);

  30:         //moveback to the charachter before

  31:         fs.Seek(-2, SeekOrigin.Current);

  32:         if (asciiText.Equals("\n"))

  33:         {

  34:             fs.Read(text, 0, 1);

  35:             asciiText = ASCIIEncoding.ASCII.GetString(text);

  36:             if (asciiText.Equals("\r"))

  37:             {

  38:                 fs.Seek(1, SeekOrigin.Current);

  39:                 break;

  40:             }

  41:         }

  42:     }

  43:     count = int.Parse((position - fs.Position).ToString());

  44:     line = new byte[count];

  45:     fs.Read(line, 0, count);

  46:     fs.Seek(-count, SeekOrigin.Current);

  47:     return ASCIIEncoding.ASCII.GetString(line);

  48: }

So, to read an open log file and dump it to the text area:

   1: protected void Button1_Click(object sender, EventArgs e)

   2: {

   3:     tbData.Text = "";

   4:     int lineCnt = 1;

   5:     List<string> lines = new List<string>();

   6:     int maxLines = int.Parse(tbLines.Text);

   7:     string logFile = Server.MapPath("~/" + ddlLogFiles.SelectedItem.ToString());

   8:     BackwardReader br = new BackwardReader(logFile);

   9:     tbData.Text = "";     while (!br.SOF)

  10:     {

  11:         string line = br.Readline();

  12:         lines.Add(line + System.Environment.NewLine);

  13:         if (lineCnt == maxLines) break;

  14:         lineCnt++;

  15:     }

  16:

  17:     for (int i = lines.Count; i > 0; i--)

  18:     {

  19:         this.tbData.Text += lines[i - 1];

  20:     }

  21: }

Initially, this worked well for a while, but I got tired of clicking “View Log” over and over and realized that I wanted a web based Tail program, like Baretail, which I use on my laptop.

Second Attempt

My next attempt was to simply use javascript’s setTimeout to refresh the page. While this worked, it seemed clunky, so I decided to ajax it. But, being a lazy programmer, I didn’t everything that WCF offers; I needed something simple and quick. I remembered reading a couple of articles about adding a static, “Webmethod” to a page. So, that’s what I did. In a previous post, I mentioned using Rick Strahl’s serviceproxy, which makes the process as easy as can be.

So the details for implementing my web based tail applications goes like this:

  • Create the ajax method as part of the web page.
  • Add serviceproxy.js, json2.js and js_extensions.js to enable ajax & displaying the logs
  • Replace the text area with a standard div; more on this below…
  • Finally some handy, dandy css to make it look nicer.

Step 1 – The Webmethod

   1: [System.Web.Services.WebMethod]

   2: public static IList<string> GetLogTail(string logname, string numrows)

   3: {

   4:     int lineCnt = 1;

   5:     List<string> lines = new List<string>();

   6:     int maxLines;

   7:

   8:     if (!int.TryParse(numrows, out maxLines))

   9:     {

  10:         maxLines = 100;

  11:     }

  12:

  13:     string logFile = HttpContext.Current.Server.MapPath("~/" + logname);

  14:

  15:     BackwardReader br = new BackwardReader(logFile);

  16:     while (!br.SOF)

  17:     {

  18:         string line = br.Readline();

  19:         lines.Add(line + System.Environment.NewLine);

  20:         if (lineCnt == maxLines) break;

  21:         lineCnt++;

  22:     }

  23:     lines.Reverse();

  24:     return lines;

  25: }

It worked much better once I added “lines.Reverse()” before returning the data. So the data returned is standard json, except there’s a “d” object that’s prepended to the data. Rick’s serviceproxy handles this, so no worries.

Step 2 – JavaScript

serviceproxy.js and json2.js are used to deal with the asp.net peculiarities of ajax of asp.net. See the links above; Rick’s site covers these in great detail.

js_extensions.js is my growing collection of javascript extension methods. Currently, this is pretty small and contains other peoples work for formatting dates and HTML encoding and decoding. My goal is to keep this small.

The heart of the whole thing is some jQuery that ties everything together, here’s the code. Instead of going through all of the code, let’s just go over a few key points:

The heavy lifting is done in the getLogData method.

function getLogData(numRows, logFile, isTail) {
    var proxy = new ServiceProxy("ViewLog.aspx/");
    proxy.isWcf = false;
    proxy.invoke("GetLogTail", { logname: logFile, numrows: numRows },
        function (data) {
            displayLog(logFile, data);
            if (isTail) {
                var method = "getLogData(" + getNumRows() + ", '" + getLogFileName() + "', "+ isTail +")";
                logTailId = setTimeout(method, getNumSecs());
            }
        },
        function (errmsg) {
            clearTimeout(logTailId);
            alert(errmsg);
        },
        false);        // NOT bare
}

As you can see, there’s not much to it. By setting “isWcf = false” and passing in false for “bare” setting, the heaving lifting is done by the serviceproxy. if successful, then display the data and call “setTimeout” for the next ajax call. Okay, so I can hear you asking, “Why not use setInterval?”. For 2 reasons, I decided to useSetTimeout: first, the precision isn’t important, and second, if the ajax method is slow, I don’t want to hammer the web set for log reading. That’s all.

If there’s an error, clearTimeout is called, so we don’t keep calling the ajax method when things to bad.

displayLog is mostly

var $log = $('#logDiv');
var logStr = "";
for (var property in data) {
    logStr += formatLine(data[property]);
}
$log.html(logStr);

So, you probably would like to see the magic that is formatLine…

function formatLine(line) {
    line = line.encodeHtml();
    if (line.toLowerCase().indexOf('info') === 0) {
        return "<span class='info'>" + line + "</span><br/>";
    } else if (line.toLowerCase().indexOf('error') === 0) {
        return "<span class='error'>" + line + "</span><br/>";
    } else if (line.toLowerCase().indexOf('warn') === 0) {
        return "<span class='warn'>" + line + "</span><br/>";
    } else if (line.toLowerCase().indexOf('debug') === 0) {
        return "<span class='debug'>" + line + "</span><br/>";
    }
    return "<span>" + line + "</span><br/>";
}

The only interesting part of this is the “encodeHtml” method, which is one of the extension methods.

So when it is all done, this is how it looks. To turn on the “tail” operation, click tail; that’s about it.log1


What’s behind MarbleSpark #2

June 16, 2010

First off, sorry for the long delay from the first installment.  MarbleSpark, my day job, family and mountain biking have all gotten in the way.  Enough excuses, let’s talk tools and libraries.  Okay, I know this is pretty boring stuff, but some folks have asked…

UI Layer

While ASP.NET and the associated page framework is at the core of the application, jQuery is what makes it rock!  I could go on and on gushing about how great jQuery is, but I’ll stick with code samples and too much text.

jQuery

Once you get over the learning curve, jQuery saves the day time and time again!.  I’m not going to give you a tutorial; go to the jQuery site and have fun.  The #1 humongous benefit of jQuery is that it isolates you from a lot of the browser differences.  I never knew how big a headache this was until I tested MarbleSpark across IE7, IE8, FireFox 2.x, FireFox 3.x, Chrome .x, Safari, etc.  Who knew that browser wars added so much work to my plate!

Enough blah, blah, let’s look at some code and this is real running code from MarbleSpark:

$(document).ready(function() {
    var birthDay = $('#<%= ddlBirthDay.ClientID %>');
    $('#<%= ddlBirthMonth.ClientID %>').change(function() { 
SetBirthDays($(this), birthDay); }).change(); // this is to force all browsers to handle the up/down keys for selects $("select").keyup(function(event) { if ((event.altKey == false) && (((event.keyCode >= 33) && (event.keyCode <= 40)) || ((event.keyCode >= 48) && (event.keyCode <= 90))) )
{ $(this).change(); // force the onChange event } return true; }); });

Let’s go over this line by line:

$(document).ready(function() { <code here> });

This one line of code provides the entry point for executing code once the page has loaded and is really ready to start work.  This isn’t the same as the body onLoad function; it is a lot smarter about knowing if the page is really ready to start running javascript.  Learn it; love it; live it!

var birthDay = $('#<%= ddlBirthDay.ClientID %>');

There’s really 2 things happening on this one line.  First, the “<%= … %>” portion is a trick to get the final DOM Element ID after asp.net mangles it.  Learn this trick, you’ll use it all the time.  Also, look at the other selectors, because you can find parents, children and a host of other approaches.  Second, the $(‘#…’) is the jQuery selector to retrieve the element by its id.  Yes, you could use document.getElementById(…), but $(‘#…’) is so much shorter and once you learn it; you’ll never go back.  Plus, jQuery allows you to chain function calls, which saves a lot of keystrokes and bytes on the wire.  I like the fluent coding style, but you don’t have to use it.

$("select").keyup(function(event) { … $(this).change(); …

Here’s an excellent example of jQuery’s power.  I found out that some browsers fire the “change” event using up and down arrow keys for selects and some don’t.  To fix the problem, I wrote what amounts to 4 lines of code.  First, the $(“select”).keyup(…) binds the anonymous function I created to ALL select elements on the page’s keyup event.  Whether there’s 1 or 100, all of them now bind to my keyup event!  Second, the “$(this).change()” line of code forces the change event to fire.  This may seem trivial, but it is very powerful.

The magic behind jQuery and event handling is that you can force an event, such as change by calling $(<selector>).change(); and wire up your own change event handler by simply specifying:

$(<selector>).change( function() { alert(‘change called’); });

It took me a bit to get used to passing around functions and often using anonymous functions, but once I did, code became simple.  I guess we can never really get away from function pointers, anonymous methods, delegates, lambdas, …

With Microsoft’s support behind jQuery, there’s little doubt in my mind for which javascript library to use.  I know there are die hard Dojo, Prototype, & name-your-library fans out there, but I’m living with jQuery and happy about it.

jQuery Plug-ins

The next huge benefit of jQuery is the ever growing library of plug-ins.  I’ve had a number of hardcore DHTML folks think that we used Flash or Silverlight for MarbleSpark, but it is just HTML with jQuery and jQuery Plug-ins.   I started out downloading tons of plug-ins and trying to make them all work and settled on the following:

  • jQuery Progress bar – This is a fairly simple progress bar that I modified it to fix a few issues related to how I built the site.  It has been so long ago that I don’t remember what I changed, but it was minor. 
  • flowplayer.org’s Overlay Component – I wish I would have found this first; I would have stopped digging.  All of the popups on the site use this and it is flip’n simple.  I highly recommend their plug-ins.
  • Clue tips – We don’t use a lot of clue tips or flyover popups, because we found them distracting.  However, I plan to replace this library with the one from flowplayer.org, because theirs just looks better.  Also, it is one library instead of two and I’m a huge fan of LIM (less is more).

There are tons and tons of plug-ins; don’t get carried away and stick with just a few or you’ll suffer from library overload! 

Misc

Here’s a few things I learned the hard way and some random tools that have made my life better for web development.  The sheer volume of tools listed below is a good indication of why there are so few really good websites; sad really.

  1. Add a version # to your javascript files to avoid browser and proxy caching headaches.  Instead of mylibrary.js, make it mylibrary-1.0.0.js.  That way, when you tweak 2 bytes of code, you rename it to mylibrary-1.0.1.js and you don’t have to tell your users to hit F5; did it work; no, okay, try Ctrl+F5…  Why I didn’t pickup up on this sooner, I don’t know.
  2. As I stated above, don’t start hacking javascript, look at jQuery and the plug-ins.  I’d hate to think what marblespark.com would look like if I hadn’t!
  3. If you don’t use Firebug, start NOW!  Being able to dynamically play with CSS and see the changes immediately, interactive javascript debugging & being able to enter javascript commands to see what happens save me hundreds and hundreds of hours.  I don’t know how anyone can build websites without this tool anymore.
  4. YSlow – This handy plug-in for Firebug provides valuable insight into things you can do to optimize your web pages.  Fair warning, it isn’t gentle on you; it is brutally honest and you better be prepared to handle the truth ;)
  5. JSLint – a little quality check of your javascript can go a long way!
  6. JSMin – Just use it and don’t complain why.  Minify your JS and you’ll save bytes and your web apps will run faster.
  7. Smush.it – is a handy dandy image compression tool from Yahoo and works really, really well.  I traded a couple of emails with the Yahoo engineers and they are very proud of this.  Use it; you won’t be disappointed.
  8. Fiddler is an http proxy that allows inspection of your http traffic.  There’s a fair amount of overlap with firebug’s “net” information, but Fiddler does more.  I’ve used the “Request Builder” feature more times than I can count for quick and dirty tests.

If you know of any excellent CSS tools, please let me know!  I’ve found CSS to be one of the biggest headaches on the planet!  Why can’t I just specify <div style=”box-align:center”> and everything in the div is centered?  ARGH!  I’ll save ranting about CSS goofiness for another post.

Framework Layer

I suspect that you’ll find the rest of this very boring, so I’m not going to go into major details.  I’m just going to list the other major libraries and let you figure out how to deal with them.  As a former hardcore Java developer, you’ll see my bias immediately!

  1. Log4Net – I find this to simply be one of the best standalone loggers out there.  The only trick that I can offer is to set the “additivity” attribute to false to avoid having all of your log messages show up in all of the logs.  Remember, log4net works in a hierarchical manner, but rarely do I want one message in multiple logs.  See below for a sample.
  2. nHibernate – What can I say; I’m an ORM convert.  When I switched from MySQL to Sql Server (long story) and I just changed one line in a config; I was sold!  If you’re a hardcore stored procedure zealot, take a look, but you’ll probably run away crying.  Once you come around to using stored procs as optimization technique only, you’ll feel better; trust me.  I want to convert from the XML mapping files to the “fluent” mappings, but I haven’t taken the time to do it.  The biggest benefit is compile time checking of mapping issues that you can’t get with the XML configs.
  3. iTextSharp – Used for PDF generation of our books.  The best advice I can give you is to buy the iText in Action book first and save yourself a lot of headaches.  If you have to generate PDFs, give this a look.  I haven’t run into any issues with it and it saved me from spending crazy money on overprices PDF libraries.
  4. I’m not using an IoC container yet.  I’ve implemented my classes following this approach, but I just don’t need that much pluggability yet.  Also, running in Medium Trust on our hosting site, breaks a lot of containers.  I am looking at Spring Framework.netWindsor & Unity for future uses.  Spring because I’m a java guy (duh), Windsor, because I read Ayende Rahein’s (Oren Eini’s) blog, and Unity, because it is supposed to work in Medium Trust; NOT because it is from Microsoft.
<logger name="klw.Framework" additivity="false" >
  <level value="INFO" />
  <appender-ref ref="FrameworkFileLog" />
  <!--    <appender-ref ref="Trace" /> -->
</logger>

Other Links


Adding a Facebook Social Plugin

May 11, 2010

A little rant

This is a classic case of something so simple on the surface turning into a royal pain.  Writing developer level docs isn’t easy, but Facebook, seriously you HAVE to do better.  First and foremost, give us all the main information AND answer your darn forums!  Okay, enough ranting at FB.

Background

Given that the world is more and more connected and user reviews are often more important than so called critics, it only makes sense for us to want to leverage the power of Facebook.  Why not add a little “Like” button on check out and help folks brag about us!  We’re not alone; Levis and other brand stores are hoping on the bandwagon. 

Implementation

First Act – “How Simple”

So, I figured, “How hard can this be?”.  After all, they have this nice page that even gives you the code you need.  Put in a few values, click the magic button, cut and paste the code and bingo DONE!  Well, not quite.  So here’s the steps:

  1. Go to Social Plugins -> Like Button.  Enter the values and select the “Get Code” button.
  2. Past this code into your web page where you want the Like button to appear.
  3. Next, go to the SDK page and copy the code provided.
  4. Go to your web page and paste this code at the bottom of the page.
  5. Since “appid” was optional and I didn’t know what it was; I just removed it.  One guess where my troubles started :D
  6. Test the page.  Whoo hoo, the “Like” button will appear.

Uploaded the page to the production site; added a brag on my facebook page and called it done.  Wow, what a genius I must be!  10 minutes to add a new facebook feature to MarbleSpark.  Phil took one look at it and said “Where’s the comment box?”, to which I replied “What comment box?”.  Now the fun begins.

Second Act – “The Struggles”

So trial and error ensues.  Nothing and I mean nothing works to make the darn comment box appear.  I added the meta tags, removed the meta tags, tried the synchronous and asynchronous java script loading techniques and still nothing worked.  Next I spent several hours digging through the forums, googled, binged and lots of folks with the same issue, but no answers.  Okay, time to swallow the male pride and “gasp” re-read the facebook documentation.  Nothing; nadda zippo.  Oh, I got it; visit a site or two that have it working; Firefox and Firebug to the rescue.  Wow, it works for these places, but wait, nothing different than what I’m doing.  Bang head against wall; repeat, repeat, …

Just about at the point to pull out my hair (darn, bald already) when I stumble across a single line in a comment on a forum about the appid.  Yes, the same appid that I removed.  Next, I find an article by Tim Ware and the light bulb went off.  I have to create a facebook application.  This sounds hard, but it is really easy.  Of course, I didn’t see any mention to this on Facebook’s developers site.

Third Act – “Finally Working”

So, to make it all work, here’s the additional steps:

  1. Create an application on Facebook developer’s site.  The MOST important thing to remember is to ensure that the site url entered matches the domain name where you are hosting it.  So “NO”, you can’t test it locally; it has to be on your real site.
  2. Add the “appid” value back into he FB.Init method.
    FB.init({ appId: ’12…’, xfbml: true, cookie: true, status: true });
  3. I went ahead and added back all the meta tags, but I couldn’t tell if they really did anything.
  4. Because of browser differences you have to ensure that the <html> tag contains
    xmlns:og="http://opengraphprotocol.org/schema/"
    xmlns:fb="http://www.facebook.com/2008/fbml"

    this caused me a bit of grief with IE.  I just punted and added the namespaces to my master page and decided to eat the extra few bytes per page.  If someone knows how to programmatically update the <html> tag in a master page, please tell me.

  5. Upload the page and bingo!

So, Facebook, why don’t you tell us in the developer docs that you have to build a frigg’n app and use the appid to make this work.  I’m sorry, but this is a huge miss.

Social sites are blocked frequently:

If you are adding any additional HTML that is specific to the like button, remember that many locations block social networking sites.  So, a little JQuery to the rescue.  I’m assuming you are using the asych approach.

  1. Ensure that all the html supporting the Like button is in a div, give it an id and set it not to display.  Here’s my div code:
    <div id="facebooklikebtn" style="display:none">
  2. In the "fbAsyncInit" function I simply added the code:
    $(‘#facebooklikebtn’).show(1000);
    so the div fades in nicely if the facebook javascript can be downloaded.

Note: I haven’t done extensive testing on this, so let me know if you have a better way of handling this.

Useful links:

  1. http://developers.facebook.com/docs/reference/plugins/like
  2. http://developers.facebook.com/docs/reference/javascript/
  3. http://www.hyperarts.com/blog/how-to-add-facebook-like-button-social-plugins-to-wordpress-posts/

What’s behind MarbleSpark

February 7, 2010

I’ve received a handful of requests for what’s behind marblespark.com.  Since we have our “secret sauce”, I won’t go into every detail, but cover the stuff that’s pretty common to most web applications.  First and foremost, what you see on the website is probably 5% of the work.  All the guts and gory details are hidden deep in the bowels of various components.

Starting Point – How’s it structured

 ms_arch

My main drivers were to do the least necessary to make it work and to provide strong separation of concerns.  If you look at this closely, you’ll notice that there’s nothing special, which is exactly what I wanted.  The only thing special about what we do is how we build the book pages and guess what; I’m not going into those details.

So let’s break this down, part by part.

User Interface

Nothing special here; asp.net’s page framework is the heart of things.  WordPress was used instead of a content management solution, because we were familiar with it and it solved our needs.  Thank you PHP support in IIS & fast CGI.  Now, I can tell you that I wish MVC was available before I started.  The page framework encourages bad programming style and I’ve caught myself slipping into bad practices once and a while.  The only thing on the web pages is navigation, validation and display logic.  That’s about it. 

Business Logic

Each major business logic category is a separate assembly.  Once again, nothing special, but a nice separation that has proven invaluable.  So the heart of the whole shoot’n match is the rendering engine.  I can tell you that I’ve spent more time on this one area than the whole rest of the project.  Since this is our secret sauce, you’re left to your own imagine here.  The rest of it is just common sense: separate each major functional area into an assembly and keep the smarts out of the UI.  I have some work to isolate things a little better.  I let some HTTP sneak into this layer and it really should be in the integration layer, but we all know that times a precious commodity.  This is a moonlight project for me after all.

Data & Integration

This was actually one of the more fun parts of the web app.  “WHAT; are you nuts?”; I hear you screaming.  I’m weird, what can I say ;)   First the data part has been so much easier than I ever imagined.  I decided to see how the .net port of hibernate worked.  Drum roll….  It worked wonderfully; actually far better than I expected.  If you’ve never tried nHibernate; take a few weeks to learn it and you’ll never look back.

eCommerce integration hasn’t been too difficult either.  I’m amazed at how easy it is now compared to 5 years ago.  Long-term, this will go away as we move to a full blown eCommerce store front.  Since we started with one product and we didn’t have the time to go through the learning curve, we just hooked up to PayPal.  Love ‘em or hate ‘em they are widely used and simple to integrate.  If you want to save a few days, you can purchase a component to do the integration.  PayPal’s developer site is okay, but their test tool for IPN is pretty much junk.  I got a lot of good information from Rick Strahl’s blog.

Now for the really hard part, printer integration.  Unfortunately, our printer uses an industry standard format where’ there’s ZERO, nada, zilcho support for .NET.  Next to the rendering engine, this was the hardest and by far the ugliest part of the project.  I ended up reverse engineering the schemas from sample XMLs. Whoever decided to design a schema with 5 levels of inheritance, everything’s optional or a choice and the XSDs are over 5 mb, please move to accounting or marketing immediately.  Bloat, bloat, bloat!  Since this is the other part of our secret sauce, I’m not going to share.  Sorry, no names here; you’ll have to find them yourself.  Like all 3rd party integrations; there’s a lot of learning and hard lessons learned once it is live. 

We’ll that’s it for now.  Next post I’ll go into more details about the tools, libraries and techniques used.  As a teaser; DOWNLOAD QUERY now!  You’ll never build a website the same way again!


First Review of marblespark.com

December 2, 2009

Here’s our first official review!  You no longer have to take our word for how wonderful our book is :D   What a wild ride; I think the best is yet to come.

So here’s the bits cut straight from http://www.thingamababy.com.

Following Featherbottom written by Phil Haussler, illustrated by Brad Sneed and web-ified by Ken Wiebke is a tribute to the child for whom it’s customized, a gift given unique to the child. There are plenty of customized books on the market, but this is the first one to catch my eye. It’s different for a lot of reasons.

Here’s the full review: http://www.thingamababy.com/baby/2009/12/featherbottom.html

Coming soon, “What’s behind MarbleSpark’s technology”; stay tuned for more…


MarbleSpark.com is alive

November 21, 2009

Over 2 1/2 years of hard work has gone into marblespark.com.  So what is it?  Glad you asked.  The site allows you to build a 100% custom book that is built from a child’s name.  What’s so cool about the books is that you get to pick the pages and no two books will be the same!  Brad Sneed the artist has done an unbelievable job on the artwork and Phil Haussler has crafted a wonderful story.  You have to see it to believe it.

So what does it take to make a book:

  1. Enter the child’s first and last name.
  2. Optionally, you can add a middle name, nickname, DOB, dedication and a sender.
  3. Click “continue” twice & wait a minute or two for you book to be rendered and downloaded.
  4. Review the book, swap in/out pages
  5. Select “Add to cart” and “Purchase” and you know the rest

Yeah, that’s all it takes.

Don’t take my word for it, here’s a quick tour of an actual book: Take the tour

So we know that there’s a few rough edges on the website.  We decided that it was better to get this out there and work through the minor issues as we go forward.  As of today, over 100 books have been printed, so don’t worry, you won’t be the first.

Here’s a lo-res teaser image…


Follow

Get every new post delivered to your Inbox.